home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / intuitin / blk.1 next >
Text File  |  1989-03-08  |  77KB  |  3,292 lines

  1. Path: xanth!lll-winken!csd4.milw.wisc.edu!mailrus!ulowell!page
  2. From: page@swan.ulowell.edu (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i036:  blk - requester layout utility
  5. Message-ID: <12054@swan.ulowell.edu>
  6. Date: 8 Mar 89 04:12:25 GMT
  7. Organization: University of Lowell, Computer Science Dept.
  8. Lines: 3281
  9. Approved: page@swan.ulowell.edu
  10.  
  11. Submitted-by: well!shf (Stuart H. Ferguson)
  12. Posting-number: Volume 89, Issue 36
  13. Archive-name: intuition/blk.1
  14.  
  15. "Blk" is a requester-generation tool to make requesters easy.  It
  16. converts a funky requester source format into 'C' declarations for
  17. including in user code.
  18.  
  19. #    This is a shell archive.
  20. #    Remove everything above and including the cut line.
  21. #    Then run the rest of the file through sh.
  22. #----cut here-----cut here-----cut here-----cut here----#
  23. #!/bin/sh
  24. # shar:    Shell Archiver
  25. #    Run the following text with /bin/sh to create:
  26. #    Makefile
  27. #    ReadMe
  28. #    blk.c
  29. #    blk.doc
  30. #    colors.r
  31. #    lex.c
  32. #    lex.h
  33. #    listmac.h
  34. #    macros.r
  35. #    pro.c
  36. #    std.h
  37. # This archive created: Tue Mar  7 23:06:09 1989
  38. cat << \SHAR_EOF > Makefile
  39. # Makefile for 'blk' utility using lex package.
  40. # Manx 3.6
  41.  
  42. blk : blk.o lex.o
  43.     ln blk.o lex.o -lc
  44.  
  45. blk.o lex.o : lex.h
  46.  
  47.  
  48. # Makefile for 'pro' demo program.  Makes 'blk' if required.
  49.  
  50. pro : pro.o
  51.     ln pro.o -lc
  52.  
  53. pro.o : prototype.h
  54.  
  55. prototype.h : colors.r blk
  56.     blk colors.r prototype.h
  57. SHAR_EOF
  58. cat << \SHAR_EOF > ReadMe
  59. IntuiTools: blk
  60.  
  61. "Blk" is a little requester-generation tool I created to make requesters
  62. easy.  It converts a funky requester source format into 'C' declarations
  63. for including in user code.  Its operation is documented in blk.doc, and
  64. an example requester is included that takes you from requester source
  65. (colors.r) to inclusion into user code (pro.c).
  66.  
  67. Have fun and happy hacking.
  68.  
  69.     Stuart Ferguson        1/89
  70.     (shf@well.UUCP)
  71.  
  72.     123 James Ave.
  73.     Redwood City, Ca.
  74.         94062
  75.  
  76.  
  77. P.S.  The lexical analysis stuff is kind of a hack -- it just proved
  78. useful for this application.  I disclaim it completely.
  79. SHAR_EOF
  80. cat << \SHAR_EOF > blk.c
  81. /*
  82.  *     blk    - Automatic Requester formatter and generator.
  83.  *
  84.  * Reads a requester description and formats graphically then generates the
  85.  * appropriate C-code to specify the requester.  See the examples and the
  86.  * documentaion for using the program.
  87.  *
  88.  * Process flow:  Box structure read by recursive-descent.  Boxes formatted
  89.  * and layed-out, also recursively.  Borders generated and optimized into a
  90.  * minimum set of Border structs.  Working preview displayed for fiddling
  91.  * with.  Output declarations written to file.
  92.  *
  93.  * Problems:  In a nutshell -- error recovery.  It doesn't do too well if
  94.  * it runs out of memory, especially in the Border optimization code.
  95.  * Also, the user error messages on parse errors could be much better --
  96.  * like giving line number or a sample of the code at that point -- something
  97.  * that would give a clue as to what went wrong.  Other than that, it's
  98.  * pretty good.
  99.  *
  100.  * Differences from version 1:  Version 1 was distributed on a Fish disk
  101.  * and was a real hack.  This version supports a 'C'-like preprocessor 
  102.  * with #include's and #define's and macros with arguments.  This makes
  103.  * the Requester source files look much nicer since the underlying 
  104.  * grammar is so unreadable.  
  105.  *
  106.  * Disclaimer: This is a tool I hacked up for my own use in creating requesters
  107.  * for Modeler 3D.  It works for me, but I make no claim as to the robustness
  108.  * or other quality of the code.  It's not mouse-driven, but it's such a
  109.  * useful tool that it's worth learning anyway.  Besides, you can put it in
  110.  * your makefile's and have it work just like any other compiler.
  111.  *
  112.  * I'm making this available as a service to Amiga developers.  You are
  113.  * encouraged to enhance or modify as you need to make it more useful for
  114.  * your own purposes.  If you make any changes that make this a better
  115.  * general-purpose tool, let me know about them.
  116.  *
  117.  *    Stuart Ferguson        1/89
  118.  *    (shf@well.UUCP)
  119.  */
  120.  
  121. #include <stdio.h>
  122. #include <functions.h>
  123. #include <exec/types.h>
  124. #include <intuition/intuition.h>
  125.  
  126. /* STD.H is my collection of useful macros.
  127.  */
  128. #include "std.h"
  129.  
  130. /* LEX.H is the lexical analysis defines.
  131.  */
  132. #include "lex.h"
  133.  
  134.  
  135. #define abs(x)          ((x)<0 ? -(x) : (x))
  136.  
  137. /* Size of font for text boxes (fixed width assumed).
  138.  */
  139. #define FONTWIDTH    8
  140. #define FONTHEIGHT    8
  141.  
  142. /* Box types
  143.  */
  144. #define HBOX    1
  145. #define VBOX    2
  146. #define FILL    3
  147. #define BLOK    4
  148. #define TEXT    5
  149. #define HRULE    6
  150. #define VRULE    7
  151.  
  152. /*
  153.  * Extended Gadget structure to hold an optional special gadget ID name
  154.  * and the parent box for this gadget.
  155.  */
  156. struct SuperGadget {
  157.     struct Gadget    g;
  158.     struct Box    *box;
  159.     char        *gnam;
  160. };
  161.  
  162. /*
  163.  * A requester is constructed from a tree of Boxes.  Boxes are arranged in a
  164.  * binary tree, one subtree is what is inside the Box (sub), the other is the
  165.  * remaining Boxes at the same level (next). 
  166.  */
  167. typedef struct Box {
  168.     struct SuperGadget    *gad;    /* gadget - if any */
  169.     struct Box     *next, *sub;    /* binary tree links */
  170.     short           type,        /* box type - possibilities above */
  171.                     col;    /* color - for borders and text */
  172.     short           xs, ys,    /* box size (x,y) */
  173.                     x, y,    /* box position (x,y) */
  174.                     nfil;    /* number of filers inside this box */
  175.     char           *val;        /* string for TEXT boxes */
  176. };
  177.  
  178.  
  179. /* GLOBAL */
  180.  
  181. int        infoLevel = 1,    /* degree of verbosity (0=quiet, 
  182.                  * 1=normal, 2=verbose)
  183.                  */
  184.         printBoxes = 0,    /* printout flag (false normally) */
  185.         showPreview;    /* preview flag (depends on whether there's
  186.                  * an output file and on the '-d' flag.
  187.                  */
  188. char           *globStr = "static ";    /* structures normally static */
  189.  
  190. FILE           *file;        /* output file */
  191. char           *base;        /* base name */
  192. short           def_bcol = 1,     /* default border and */
  193.         def_tcol = 1;    /* text colors */
  194.  
  195. /* String pointer ID's returned from the lexer.
  196.  */
  197. char           *fill_pstr, *tbox_pstr, *hbox_pstr, *vbox_pstr,
  198.            *blok_pstr, *s_pstr, *p_pstr, *pv_pstr, *ph_pstr;
  199.  
  200. /* The Requester structures, the lists of parts and the guy itself.
  201.  */
  202. struct Border    *blst = NULL;    /* list header for border structs */
  203. struct IntuiText *itlst = NULL;    /* list header for text structs */
  204. struct Gadget    *glst = NULL;    /* list header for gadgets */
  205.  
  206. #define LATER    0
  207.  
  208. struct Requester mreq = {
  209.     NULL,5,5,LATER,LATER,0,0,LATER,LATER,LATER,
  210.     0,0,NULL,{NULL},NULL,NULL,{NULL}
  211. };
  212.  
  213. /*
  214.  * Generic templates for creating Intuition structures.
  215.  */
  216. struct Border
  217.     generic_border = {0, 0, LATER, 0, JAM1, LATER, LATER, LATER};
  218. struct Gadget
  219.     generic_gadget = {
  220.         LATER, LATER, LATER, LATER, LATER,
  221.         GADGHCOMP,RELVERIFY,BOOLGADGET|REQGADGET,
  222.         LATER, NULL, LATER, 0, LATER, LATER, NULL
  223.     };
  224. struct IntuiText
  225.     generic_itext = {LATER, 0, JAM2, LATER, LATER, LATER, LATER, LATER};
  226. struct StringInfo
  227.     generic_sinfo = {LATER, NULL, 0, LATER, 0};
  228. struct PropInfo
  229.     generic_pinfo = {AUTOKNOB|PROPBORDERLESS,0x8000,0x8000,0x8000,0x8000};
  230.  
  231. /* Two macros to extract GadgetType info from a Gadget structure.
  232.  */
  233. #define GADGETTYPEBITS    (~GADGETTYPE)
  234. #define GTYPE(g)    (((g)->GadgetType)&GADGETTYPEBITS)
  235.  
  236. /*
  237.  * The preview window.
  238.  */
  239. struct NewWindow nwin = {
  240.     0, 0, 300, 150, -1, -1,
  241.     CLOSEWINDOW | REQCLEAR | MOUSEMOVE | GADGETDOWN | GADGETUP | VANILLAKEY,
  242.     WINDOWDEPTH | WINDOWDRAG,
  243.     NULL, NULL, (UBYTE *) "Preview", NULL,
  244.     NULL, 0, 0, 0, 0, WBENCHSCREEN
  245. };
  246.  
  247. struct IntuitionBase *IntuitionBase;
  248.  
  249.  
  250. struct Box * ReadBoxList ();
  251. short Layin ();
  252. char * IMClass ();
  253.  
  254. /* Lexer interface functions.
  255.  */
  256. char * FindString ();
  257. short NextToken ();
  258.  
  259.  
  260. /*
  261.  * Returns a new, initialized Box struct.
  262.  */
  263. struct Box * NewBox (type)
  264.     short           type;
  265. {
  266.     struct Box     *b;
  267.  
  268.     if (!(b = NEW (struct Box))) {
  269.         MemError ();
  270.         return NULL;
  271.     }
  272.     b->type = type;
  273.     b->nfil = 0;
  274.     b->gad = NULL;
  275.     b->val = NULL;
  276.     b->next = b->sub = NULL;
  277.     return b;
  278. }
  279.  
  280.  
  281. /*
  282.  * Recursively frees Box tree.
  283.  */
  284. FreeBox (box)
  285.     struct Box *box;
  286. {
  287.     register struct Gadget    *g;
  288.     register struct StringInfo *si;
  289.  
  290.     if (!box) return;
  291.  
  292.     FreeBox (box->sub);
  293.     FreeBox (box->next);
  294.  
  295.     if (g = (struct Gadget *) box->gad) {
  296.         if (GTYPE(g) == STRGADGET) {
  297.             si = (struct StringInfo *) g->SpecialInfo;
  298.             FREE_X (si, struct StringInfo, si->MaxChars);
  299.         } else if (GTYPE(g) == PROPGADGET) {
  300.             FREE (g->SpecialInfo, struct PropInfo);
  301.             FREE (g->GadgetRender, struct Image);
  302.         }
  303.         FREE (g, struct SuperGadget);
  304.     }
  305.     FREI (box);
  306. }
  307.  
  308.  
  309. /*
  310.  * Recursively examine all nodes of Box tree and allocate Border structs for
  311.  * all the HRULE and VRULE boxes.  Adds new Borders to the main list. 
  312.  * Returns 0 for failure, 1 for sucess.
  313.  */
  314. int CreateBorder (box)
  315.     register struct Box    *box;
  316. {
  317.     register struct Border *bd;
  318.  
  319.     if (!box) return 1;
  320.  
  321.     if (box->type == HRULE || box->type == VRULE) {
  322.         if (!(bd = NEW (struct Border))) {
  323.             MemError ();
  324.             FreeBorder ();
  325.             return 0;
  326.         }
  327.         *bd = generic_border;
  328.         bd->FrontPen = box->col;
  329.         bd->Count = 2;
  330.         if (!(bd->XY = NEW_N (SHORT, 4))) {
  331.             MemError ();
  332.             FREI (bd);
  333.             FreeBorder ();
  334.             return 0;
  335.         }
  336.         bd->XY[0] = bd->XY[2] = box->x;
  337.         bd->XY[1] = bd->XY[3] = box->y;
  338.         if (box->type == HRULE) bd->XY[2] += box->xs - 1;
  339.           else bd->XY[3] += box->ys - 1;
  340.  
  341.         bd->NextBorder = blst;
  342.         blst = bd;
  343.     }
  344.     if (!CreateBorder (box->sub)) return 0;
  345.     return (CreateBorder (box->next));
  346. }
  347.  
  348.  
  349. /*
  350.  * Frees all Border structs in main Border list.
  351.  */
  352. FreeBorder ()
  353. {
  354.     register struct Border *b, *nxt;
  355.  
  356.     for (b = blst; b; b = nxt) {
  357.         nxt = b->NextBorder;
  358.         FREE_N (b->XY, SHORT, b->Count * 2);
  359.         FREI (b);
  360.     }
  361.     blst = NULL;
  362. }
  363.  
  364.  
  365. /*
  366.  * Recursively examine all nodes of Box tree and allocate IntuiText structs
  367.  * for TEXT boxes that are not string gadgets.  Adds new Borders to the main
  368.  * list.  Returns 1 for sucess, 0 for failure.
  369.  */
  370. int CreateIText (box)
  371.     register struct Box     *box;
  372. {
  373.     struct IntuiText    *it;
  374.  
  375.     if (!box) return 1;
  376.  
  377.     /*
  378.      * "box->val" may have been zero-ed by a string gadget grabbing
  379.      * that text.  If so, this is not an IntuiText.
  380.      */
  381.     if (box->type == TEXT && box->val) {
  382.         if (!(it = NEW(struct IntuiText))) {
  383.             MemError ();
  384.             FreeIText ();
  385.             return 0;
  386.         }
  387.         *it = generic_itext;
  388.         it->IText = (UBYTE *) box->val;
  389.         it->LeftEdge = box->x;
  390.         it->TopEdge = box->y;
  391.         it->FrontPen = box->col;
  392.         it->NextText = itlst;
  393.         itlst = it;
  394.     }
  395.     if (!CreateIText (box->sub)) return 0;
  396.     return (CreateIText (box->next));
  397. }
  398.  
  399.  
  400. /*
  401.  * Frees all IntuiText structs in the main list.  (No need to free the
  402.  * text itself since that is managed by the lexer.)
  403.  */
  404. FreeIText ()
  405. {
  406.     register struct IntuiText *it, *nxt;
  407.  
  408.     for (it = itlst; it; it = nxt) {
  409.         nxt = it->NextText;
  410.         FREI (it);
  411.     }
  412.     itlst = NULL;
  413. }
  414.  
  415.  
  416. /*
  417.  * First pass at merging redundant Borders:  Examines all the Borders in
  418.  * the list for adjacency.  Any borders that could use the same set of
  419.  * polyline commands are merged into a single struct. 
  420.  */
  421. MergeBorders ()
  422. {
  423.     register struct Border *a, *b;
  424.     short           i0, i1, x, y, *xy, j;
  425.     register short  i, ac, bc, merge;
  426.  
  427.     do {
  428.         merge = -1;
  429.         /*
  430.          * Examine all pairs of borders, "a" and "b", that
  431.          * are drawn with the same color, seaching for a pair
  432.          * that can be merged.  When loop exits with merge=-1,
  433.          * all pairs have been merged.
  434.          */
  435.         for (a = blst; a; a = a->NextBorder) {
  436.             for (b = a->NextBorder; b; b = b->NextBorder) {
  437.                 if (a->FrontPen != b->FrontPen) continue;
  438.  
  439.                 /*
  440.                  * Examine the 4 pairs of endpoints of each
  441.                  * polyline to see if any are adjacent to
  442.                  * each other.  If any are found, the pairs
  443.                  * located are encoded into "merge" and
  444.                  * the search loop exits.
  445.                  */
  446.                 ac = a->Count;
  447.                 bc = b->Count;
  448.                 for (i0 = 0; i0 < 2; i0++)
  449.                     for (i1 = 0; i1 < 2; i1++) {
  450.                         x = a->XY[i0*2 * (ac - 1)]
  451.                           - b->XY[i1*2 * (bc - 1)];
  452.                         y = a->XY[i0*2 * (ac - 1) + 1]
  453.                           - b->XY[i1*2 * (bc - 1) + 1];
  454.                         if (abs (x) + abs (y) == 1)
  455.                             merge = (i0 << 1) + i1;
  456.                     }
  457.                 if (merge != -1)
  458.                     break;
  459.             }
  460.             if (merge != -1)
  461.                 break;
  462.         }
  463.         if (merge == -1) continue;
  464.  
  465.         /*
  466.          * Merging: Create a new polyline data array and move
  467.          * the two parent polylines into the new one, possibly
  468.          * reversing one or both in the process.
  469.          * -- HELP ME:    Is there a nice way out if this
  470.          *        allocation fails...?
  471.          */
  472.         xy = NEW_N (SHORT, (bc + ac) * 2);
  473.         x = ((merge & 2) == 0);        /* x = reverse "a" */
  474.         y = ((merge & 1) == 1);        /* y = reverse "b" */
  475.         j = 0;
  476.         for (i = 0; i < ac; i++) {
  477.             i0 = (x ? ac - 1 - i : i) * 2;
  478.             xy[j++] = a->XY[i0];
  479.             xy[j++] = a->XY[i0 + 1];
  480.         }
  481.         for (i = 0; i < bc; i++) {
  482.             i0 = (y ? bc - 1 - i : i) * 2;
  483.             xy[j++] = b->XY[i0];
  484.             xy[j++] = b->XY[i0 + 1];
  485.         }
  486.  
  487.         /*
  488.          * Set "a" to have the new polyline data array.
  489.          */
  490.         a->Count = j / 2;
  491.         FREE_N (a->XY, SHORT, ac * 2);
  492.         a->XY = xy;
  493.  
  494.         /*
  495.          * Find "b's" predecessor and remove "b" from list.
  496.          */
  497.         for (a = blst; a && a->NextBorder != b; a = a->NextBorder);
  498.         a->NextBorder = b->NextBorder;
  499.         FREE_N (b->XY, SHORT, bc * 2);
  500.         FREE (b, struct Border);
  501.  
  502.     } while (merge != -1);
  503. }
  504.  
  505.  
  506. /*
  507.  * Second pass of Border merging: Eliminates linear segments from all
  508.  * Borders XY lists.  The first pass will create lots of redundant points
  509.  * along linear line segments.  This pass will compress those out.
  510.  */
  511. MergeLinear ()
  512. {
  513.     register struct Border *b;
  514.     register short  i0, i1, i2, k, *xy;
  515.  
  516.     /*
  517.      * Examine all borders with more than 1 line segment.
  518.      */
  519.     for (b = blst; b; b = b->NextBorder) {
  520.         if (b->Count < 3) continue;
  521.  
  522.         /*
  523.          * Scan along the polyline list and compress out linear
  524.          * segments by skiping over them.
  525.          */
  526.         xy = b->XY;
  527.         i0 = 0;
  528.         i1 = 1;
  529.         i2 = 2;
  530.         k = 2;
  531.         while (i2 < b->Count) {
  532.             /*
  533.              * Skip past linear segments. (I.e. find the bend.)
  534.              */
  535.             while (i2 < b->Count &&
  536.                    (xy[i0 * 2] == xy[i1 * 2]
  537.                  && xy[i1 * 2] == xy[i2 * 2] ||
  538.                 xy[i0 * 2 + 1] == xy[i1 * 2 + 1]
  539.                  && xy[i1 * 2 + 1] == xy[i2 * 2 + 1])) {
  540.                 i1++;
  541.                 i2++;
  542.             }
  543.             if (i2 >= b->Count) continue;
  544.  
  545.             /*
  546.              * Move polyline data to itself after skipping.
  547.              */
  548.             xy[k++] = xy[i1 * 2];
  549.             xy[k++] = xy[i1 * 2 + 1];
  550.             i0 = i1;
  551.             i1 = i2;
  552.             i2 = i1 + 1;
  553.         }
  554.         xy[k++] = xy[i1 * 2];
  555.         xy[k++] = xy[i1 * 2 + 1];
  556.  
  557.         k /= 2;
  558.         if (k == b->Count) continue;
  559.  
  560.         /*
  561.          * If this border has gotten shorter, allocate a new
  562.          * array and transfer the new polyline data.
  563.          */
  564.         xy = NEW_N (SHORT, k * 2);
  565.         for (i0 = 0; i0 < k * 2; i0++) xy[i0] = b->XY[i0];
  566.         FREE_N (b->XY, SHORT, b->Count * 2);
  567.         b->XY = xy;
  568.         b->Count = k;
  569.     }
  570. }
  571.  
  572.  
  573. /*
  574.  * Set the XSize and YSize fields for this box and all below.
  575.  */
  576. Format (box)
  577.     struct Box     *box;
  578. {
  579.     struct Box     *b;
  580.     short           mx, my, sx, sy, nf;
  581.  
  582.     ASSERT (box);
  583.  
  584.     /*
  585.      * Deal with the basis (leaf) cases.
  586.      */
  587.     switch (box->type) {
  588.     
  589.         /* Blok and text nodes have fixed, already computed size.
  590.          */
  591.         case BLOK:
  592.         case TEXT:
  593.         return;
  594.  
  595.         /* Fill node has no intrinsic size.
  596.          */
  597.         case FILL:
  598.         box->xs = box->ys = 0;
  599.         box->nfil = 1;
  600.         return;
  601.  
  602.         /* H and VRULES have no intrinsic X or Y size, respectively.
  603.          */
  604.         case HRULE:
  605.         box->xs = 0;
  606.         return;
  607.         case VRULE:
  608.         box->ys = 0;
  609.         return;
  610.     }
  611.  
  612.     /*
  613.      * H and VBOXes are the recursive case.  First format each
  614.      * internal box.
  615.      */
  616.     for (b = box->sub; b; b = b->next) Format (b);
  617.  
  618.     /*
  619.      * Compute total and max sizes in each direction. Total (sx,sy) is sum
  620.      * of all sub-boxes, max (mx,my) is max of sub-boxes. Also inherit
  621.      * filler count.
  622.      */
  623.     my = mx = sx = sy = nf = 0;
  624.     for (b = box->sub; b; b = b->next) {
  625.         sx += b->xs;
  626.         sy += b->ys;
  627.         if (b->type == box->type || b->type == FILL) nf += b->nfil;
  628.         if (b->xs > mx) mx = b->xs;
  629.         if (b->ys > my)    my = b->ys;
  630.     }
  631.     box->nfil = nf;
  632.  
  633.     /*
  634.      * For horizontal boxes, bounding box is sum in x and max in y; for
  635.      * vertical, bouding box is max in x and sum in y.  This is the
  636.      * minimum size of the containing box for the given subboxes.  It
  637.      * may still expand due to fillers.
  638.      */
  639.     if (box->type == HBOX) {
  640.         box->xs = sx;
  641.         box->ys = my;
  642.     } else if (box->type == VBOX) {
  643.         box->xs = mx;
  644.         box->ys = sy;
  645.     }
  646. }
  647.  
  648.  
  649. /*
  650.  * Compute the position of the boxes internal to this box given that this
  651.  * box has correct location.  The box size computed by Format() is a minimum
  652.  * size, "mx" and "my" are the max that the box can be expanded by filler.
  653.  */
  654. Layout (box, mx, my)
  655.     struct Box     *box;
  656.     short           mx, my;
  657. {
  658.     struct Box     *b;
  659.     short           ish, z, nfil;
  660.     long            gap, ifil;
  661.  
  662.     ASSERT (box);
  663.  
  664.     /*
  665.      * Rules fill out to their max possible size.
  666.      */
  667.     if (box->type == HRULE) box->xs = mx;
  668.      else if (box->type == VRULE) box->ys = my;
  669.  
  670.     /*
  671.      * Process only HBOX and VBOX cases recursively.  Any other case (a
  672.      * basis case) has its position set correctly (see assumptions at
  673.      * head of function).
  674.      */
  675.     if (box->type != HBOX && box->type != VBOX) return;
  676.  
  677.     /* Get important values.  Set the "is-hbox" (ish) flag.  Get the
  678.      * current X size for HBOXes or the Y size for VBOXes as "z".
  679.      * "gap" is the differece between the max size and minimum size 
  680.      * given by the Format(), and is how much fillers can expand.
  681.      */
  682.     ish = (box->type == HBOX);
  683.     z = (ish ? box->x : box->y);
  684.     gap = (ish ? mx - box->xs : my - box->ys);
  685.  
  686.     /*
  687.      * Set positions by setting filler sizes.
  688.      */
  689.     ifil = 0;
  690.     Layin (box, &ifil, ish, z, box->nfil, gap);
  691.  
  692.     /* Update box size.  If it had fillers, it got as big as
  693.      * it could.
  694.      */
  695.     if (box->nfil) {
  696.         if (ish) box->xs = mx;
  697.             else box->ys = my;
  698.     }
  699. }
  700.  
  701.  
  702. /*
  703.  * Layout internal boxes.  Having this as a recursive function deals with
  704.  * the case of VBOXes within VBOXes and HBOXes within HBOXes.
  705.  *
  706.  * NOTE: I'd comment this function, but I can't figure it out.  It seems to
  707.  * figure out the horizonal position of each box and update it as it goes
  708.  * along.  It also calls itself when there are nested same-class boxes.
  709.  * Oh well.  There's probably a better way to do it anyway.
  710.  */
  711. short Layin (box, ifil, ish, z, nfil, gap)
  712.     struct Box     *box;
  713.     short          *ifil, ish, z, nfil;
  714.     long            gap;
  715. {
  716.     struct Box     *b;
  717.     short           t;
  718.  
  719.     for (b = box->sub; b; b = b->next) {
  720.         if (ish) {
  721.             b->x = z;
  722.             b->y = box->y;
  723.         } else {
  724.             b->x = box->x;
  725.             b->y = z;
  726.         }
  727.  
  728.         if (b->type == FILL) {
  729.             t = (gap * (*ifil + 1)) / nfil - (gap ** ifil) / nfil;
  730.             (*ifil)++;
  731.             if (ish) b->xs = t;
  732.                 else b->ys = t;
  733.  
  734.         } else if ((ish && b->type == HBOX)
  735.                || (!ish && b->type == VBOX)) {
  736.             if (ish) b->ys = box->ys;
  737.                 else b->xs = box->xs;
  738.             t = Layin (b, ifil, ish, z, nfil, gap) - z;
  739.             if (ish) b->xs = t;
  740.                 else b->ys = t;
  741.  
  742.         } else Layout (b, box->xs, box->ys);
  743.  
  744.         z += (ish ? b->xs : b->ys);
  745.     }
  746.     return z;
  747. }
  748.  
  749.  
  750. /*
  751.  * Use the computed position of the boxes to set the position of
  752.  * the associated gadgets.
  753.  */
  754. PositionGadgets ()
  755. {
  756.     struct Box    *b;
  757.     struct Gadget    *g;
  758.  
  759.     for (g = glst; g; g = g->NextGadget) {
  760.         b = ((struct SuperGadget *) g)->box;
  761.         g->LeftEdge = b->x;
  762.         g->TopEdge = b->y;
  763.         g->Width = b->xs;
  764.         g->Height = b->ys;
  765.     }
  766. }
  767.  
  768.  
  769. /*
  770.  * Returns pointer to string containing box type name for printout.
  771.  */
  772. char * BoxType (typ)
  773.     short           typ;
  774. {
  775.     switch (typ) {
  776.         case HBOX:    return ("HBOX");
  777.         case VBOX:    return ("VBOX");
  778.         case BLOK:    return ("BLOK");
  779.         case TEXT:    return ("TEXT");
  780.         case FILL:    return ("FILL");
  781.         case HRULE:    return ("HRULE");
  782.         case VRULE:    return ("VRULE");
  783.     }
  784. }
  785.  
  786.  
  787. /*
  788.  * Recursively prints this box and all its contents.
  789.  */
  790. PrintBox (box, lev)
  791.     struct Box     *box;
  792.     short           lev;
  793. {
  794.     int             i;
  795.  
  796.     if (!box) return;
  797.  
  798.     for (i = 0; i < lev; i++) printf ("  ");
  799.  
  800.     printf ("%s (%d,%d) %dx%d", BoxType (box->type),
  801.         box->x, box->y, box->xs, box->ys);
  802.     if (box->type == TEXT)    printf (" <%s>", box->val);
  803.     if (box->gad)         printf (" [gadget]");
  804.     printf ("\n");
  805.  
  806.     PrintBox (box->sub, lev + 1);
  807.     PrintBox (box->next, lev);
  808. }
  809.  
  810.  
  811.  
  812. /*
  813.  * ==== INPUT SECTION ====
  814.  *
  815.  * File input uses the "lex" front-end for macro processing.  Main entry
  816.  * points for this package are the NextToken() and Backspace() functions.
  817.  * NextToken() returns the code for the next lexical item in the input 
  818.  * stream and sets a buffer pointer to point to its value.  Backspace()
  819.  * resets the lex package to re-read the last token read, so that the
  820.  * file is effectively backspaced one token.  FindString() is also used
  821.  * to get the unique identifer pointer for a string from the hash table.
  822.  */
  823.  
  824.  
  825. /*
  826.  * Read a number if there is one.  Otherwise return false and don't
  827.  * change n's value.
  828.  */
  829. BOOL Qnum (n, radix)
  830.     short          *n, radix;
  831. {
  832.     short           i = 0, tok;
  833.     char           *buf;
  834.  
  835.     tok = NextToken (&buf);
  836.     if (tok != RT_NUM) {
  837.         Backspace ();
  838.         return 0;
  839.     }
  840.     for (; *buf >= '0' && *buf <= '9'; buf++) {
  841.         i = i * radix + (*buf - '0');
  842.     }
  843.     *n = i;
  844.     return 1;
  845. }
  846.  
  847.  
  848. /*
  849.  * Reads a double-quoted string like
  850.  *    "stuff"
  851.  * from the file.  Returns pointer to the string contents. 
  852.  */
  853. char * ReadString ()
  854. {
  855.     short           tok;
  856.     char           *buf;
  857.  
  858.     tok = NextToken (&buf);
  859.     if (tok != RT_STR) {
  860.         fprintf (stderr, "String not found.\n");
  861.         Backspace ();
  862.         return NULL;
  863.     }
  864.     return buf;
  865. }
  866.  
  867.  
  868. /*
  869.  * Read gadget ID of the form 
  870.  *    :number
  871.  * if there is one.  Read as hex.  If there is one, create a new
  872.  * SuperGadget structure and add to the main gadget list.
  873.  */
  874. struct SuperGadget * ReadOptGadget (box)
  875.     struct Box *box;
  876. {
  877.     struct SuperGadget *sg;
  878.     short           tok, id;
  879.     char           *buf;
  880.  
  881.     tok = NextToken (&buf);
  882.     if (tok != RT_CHR || *buf != ':') {
  883.         Backspace ();
  884.         return NULL;
  885.     }
  886.     if (!Qnum (&id, 16)) {
  887.         fprintf (stderr, "Error reading gadget ID number\n");
  888.         return NULL;
  889.     }
  890.  
  891.     if (!(sg = NEW (struct SuperGadget))) {
  892.         MemError ();
  893.         return NULL;
  894.     }
  895.     sg->g = generic_gadget;
  896.     sg->gnam = NULL;
  897.     sg->box = box;
  898.     sg->g.GadgetID = id;
  899.     sg->g.NextGadget = glst;
  900.     glst = (struct Gadget *) sg;
  901.     return sg;
  902. }
  903.  
  904.  
  905. /*
  906.  * Get a box from the open file.  Boxes are either single tokens ("f"
  907.  * for FILL box, "-" and "|" for ordinary rules) or is a
  908.  * composite of the form "("type data")".  Type can be "h" for HBOX,
  909.  * "v" for VBOX, "b" for BLOK, "t" for TEXT, or "-" and "|" again for 
  910.  * special rules.
  911.  *
  912.  * If there isn't a box here, ReadBox() returns NULL with the lexical
  913.  * stream positioned back to read whatever was really there.
  914.  */
  915. struct Box * ReadBox ()
  916. {
  917.     short           tok, i;
  918.     char           *buf, c;
  919.     struct Box     *b;
  920.  
  921.     tok = NextToken (&buf);
  922.  
  923.     if (tok == RT_ID && buf == fill_pstr) return NewBox (FILL);
  924.  
  925.     if (tok != RT_CHR) {
  926.         Backspace ();
  927.         return NULL;
  928.     }
  929.  
  930.     c = *buf;
  931.     if (c == '-') {
  932.         if (!(b = NewBox (HRULE))) return NULL;
  933.         b->ys = 1;
  934.         b->col = def_bcol;
  935.         return b;
  936.     }
  937.     if (c == '|') {
  938.         if (!(b = NewBox (VRULE))) return NULL;
  939.         b->xs = 1;
  940.         b->col = def_bcol;
  941.         return b;
  942.     }
  943.     if (c != '(') {
  944.         Backspace ();
  945.         return NULL;
  946.     }
  947.  
  948.     /*
  949.      * Decode the value inside the '('.
  950.      */
  951.     tok = NextToken (&buf);
  952.     c = *buf;
  953.     if (tok == RT_ID)
  954.         if (buf == hbox_pstr) {
  955.             if (!(b = NewBox (HBOX))) return NULL;
  956.             b->sub = ReadBoxList ();
  957.         } else if (buf == vbox_pstr) {
  958.             if (!(b = NewBox (VBOX))) return NULL;
  959.             b->sub = ReadBoxList ();
  960.         } else if (buf == tbox_pstr) {
  961.             if (!(b = NewBox (TEXT))) return NULL;
  962.             b->col = def_tcol;
  963.             Qnum (&b->col, 10);
  964.             if (!(b->val = ReadString ())) {
  965.                 FreeBox (b);
  966.                 return NULL;
  967.             }
  968.             b->xs = strlen (b->val) * FONTWIDTH;
  969.             b->ys = FONTHEIGHT;
  970.         } else if (buf == blok_pstr) {
  971.             if (!(b = NewBox (BLOK))) return NULL;
  972.             i = Qnum (&b->xs, 10);
  973.             i &= Qnum (&b->ys, 10);
  974.             if (!i) {
  975.                 fprintf (stderr, "Block needs X and Y sizes\n");
  976.                 return NULL;
  977.             }
  978.         } else {
  979.             fprintf (stderr, "Unrecognized box type <%s>\n", buf);
  980.             return NULL;
  981.         }
  982.     else if (tok == RT_CHR)
  983.         switch (c) {
  984.             case '-':
  985.             if (!(b = NewBox (HRULE))) return NULL;
  986.             if (!Qnum (&b->ys, 10)) {
  987.                 fprintf (stderr, "Bad hrule structure.\n");
  988.                 return NULL;
  989.             }
  990.             b->col = def_bcol;
  991.             Qnum (&b->col, 10);
  992.             break;
  993.             case '|':
  994.             if (!(b = NewBox (VRULE))) return NULL;
  995.             if (!Qnum (&b->xs, 10)) {
  996.                 fprintf (stderr, "Bad vrule structure\n");
  997.                 return NULL;
  998.             }
  999.             b->col = def_bcol;
  1000.             Qnum (&b->col, 10);
  1001.             break;
  1002.             default:
  1003.             fprintf (stderr, "Unrecognized box type <%c>\n", c);
  1004.             return NULL;
  1005.         }
  1006.     else {
  1007.         fprintf (stderr, "Unrecognized box type <%s>\n", buf);
  1008.         return NULL;
  1009.     }
  1010.     /*
  1011.      * Pick up the closing ')'.
  1012.      */
  1013.     tok = NextToken (&buf);
  1014.     if (tok != RT_CHR || *buf != ')') {
  1015.         fprintf (stderr, "Parse error - expected ')' !\n");
  1016.         FreeBox (b);
  1017.         return NULL;
  1018.     }
  1019.  
  1020.     /*
  1021.      * Read the optional Gadget for this box (as ":id").
  1022.      */
  1023.     b->gad = ReadOptGadget (b);
  1024.     return b;
  1025. }
  1026.  
  1027.  
  1028. /*
  1029.  * Read a list of boxes from the file stream.  Recursive: read a box,
  1030.  * then read a list. 
  1031.  */
  1032. struct Box * ReadBoxList ()
  1033. {
  1034.     struct Box      *b;
  1035.  
  1036.     b = ReadBox ();
  1037.     if (!b) return NULL;
  1038.  
  1039.     b->next = ReadBoxList ();
  1040.     return b;
  1041. }
  1042.  
  1043.  
  1044. /*
  1045.  * Create a new StringInfo struct and initialize to point to the 
  1046.  * given string buffer.  Allocates space for the buffer along with
  1047.  * the info struct itself (NEW_X).  Removes trailing spaces.
  1048.  */
  1049. APTR NewStrInfo (buf)
  1050.     char *buf;
  1051. {
  1052.     struct StringInfo    *si;
  1053.     short            i;
  1054.     char            *str;
  1055.  
  1056.     i = strlen (buf) + 1;
  1057.     if (!(si = NEW_X (struct StringInfo, i))) {
  1058.         MemError ();
  1059.         return NULL;
  1060.     }
  1061.     *si = generic_sinfo;
  1062.     si->Buffer = (UBYTE *) (str = (char *) (si+1));
  1063.     si->MaxChars = i;
  1064.     strcpy (str, buf);
  1065.     for (i -= 2; i>=0 && str[i] == ' '; i--) str[i] = 0;
  1066.     return (APTR) si;
  1067. }
  1068.  
  1069.  
  1070. /* Create new PropInfo struct.  Set the free motion flag based on the
  1071.  * id for this gadget "pv" = vert prop, "ph" = horiz prop, "p" = h+v prop.
  1072.  */
  1073. APTR NewPropInfo (id)
  1074.     char *id;
  1075. {
  1076.     register struct PropInfo    *pi;
  1077.  
  1078.     if (!(pi = NEW (struct PropInfo))) {
  1079.         MemError ();
  1080.         return NULL;
  1081.     }
  1082.     *pi = generic_pinfo;
  1083.     if (id == p_pstr || id == pv_pstr) pi->Flags |= FREEVERT;
  1084.     if (id == p_pstr || id == ph_pstr) pi->Flags |= FREEHORIZ;
  1085.     return (APTR) pi;
  1086. }
  1087.  
  1088.  
  1089. /*
  1090.  * Reads the list of gadget info from the end of the file.  Reads as much
  1091.  * as there is.  Format is:
  1092.  *    number {s|p|pv|ph} {:string} string
  1093.  * stuff in {}'s is optional.  Each entry gives extra info for the numbered
  1094.  * gadget.  {s|p} is string or prop flag.  {:string} is the optional named
  1095.  * value rather than just the nubmer.  The last string is the gadget flags.
  1096.  * Each set of info gets added to the corresponding gadget structure in
  1097.  * the main list.
  1098.  */
  1099. ReadGadInfo ()
  1100. {
  1101.     struct Gadget  *g;
  1102.     struct Box     *box;
  1103.     short           tok;
  1104.     char           *buf, c, *actf;
  1105.     short           i;
  1106.     USHORT        flag;
  1107.  
  1108.     while (Qnum (&i, 16)) {
  1109.         /*
  1110.          * Locate the gadget in question and it's associated box.
  1111.          */
  1112.         for (g = glst; g; g = g->NextGadget)
  1113.             if (g->GadgetID == i) break;
  1114.         if (!g) {
  1115.             fprintf (stderr, "Unknown gadget ID: %x\n", i);
  1116.             continue;
  1117.         }
  1118.         box = ((struct SuperGadget *) g)->box;
  1119.  
  1120.         /* Get the optional string or prop flag.
  1121.          */
  1122.         tok = NextToken (&buf);
  1123.         if (tok == RT_ID) {
  1124.             if (buf == s_pstr) {
  1125.                 g->GadgetType &= ~GADGETTYPEBITS;
  1126.                 g->GadgetType |= STRGADGET;
  1127.                 if (!(g->SpecialInfo = NewStrInfo (box->val)))
  1128.                     return;
  1129.                 box->val = NULL;
  1130.             } else if (buf == p_pstr
  1131.                 || buf == ph_pstr
  1132.                 || buf == pv_pstr) {
  1133.                 g->GadgetType &= ~GADGETTYPEBITS;
  1134.                 g->GadgetType |= PROPGADGET;
  1135.                 if (!(g->SpecialInfo = NewPropInfo (buf)))
  1136.                     return;
  1137.                 if (!(g->GadgetRender =
  1138.                     (APTR) NEW (struct Image))) {
  1139.                     MemError ();
  1140.                     FREE (g->SpecialInfo, struct PropInfo);
  1141.                     return;
  1142.                 }
  1143.             } else {
  1144.                 fprintf (stderr,
  1145.                     "Expected \"s\" or \"p\": <%s>\n", buf);
  1146.                 break;
  1147.             }
  1148.             tok = NextToken (&buf);
  1149.         }
  1150.  
  1151.         /* Get optional gadget ID name string.
  1152.          */
  1153.         if (tok == RT_CHR && *buf == ':') {
  1154.             ((struct SuperGadget *) g)->gnam = ReadString ();
  1155.             tok = NextToken (&buf);
  1156.         }
  1157.         Backspace ();
  1158.  
  1159.         /* Get and process required activation flags string.
  1160.          */
  1161.         actf = ReadString ();
  1162.         g->Activation &= ~RELVERIFY;
  1163.         for (; *actf; actf++) {
  1164.             switch (*actf) {
  1165.                 case 'B':
  1166.                 g->Flags &= ~GADGHIGHBITS;
  1167.                 g->Flags |= GADGHBOX;
  1168.                 flag = 0;
  1169.                 break;
  1170.                 case 't':
  1171.                 flag = TOGGLESELECT;
  1172.                 break;
  1173.                 case 'v':
  1174.                 flag = RELVERIFY;
  1175.                 break;
  1176.                 case 'e':
  1177.                 flag = ENDGADGET;
  1178.                 break;
  1179.                 case 'i':
  1180.                 flag = GADGIMMEDIATE;
  1181.                 break;
  1182.                 case 'c':
  1183.                 flag = STRINGCENTER;
  1184.                 break;
  1185.                 case 'f':
  1186.                 flag = FOLLOWMOUSE;
  1187.                 break;
  1188.             }
  1189.             g->Activation |= flag;
  1190.         }
  1191.     }
  1192. }
  1193.  
  1194.  
  1195. /*
  1196.  * Get values for the identifier strings from the lexical analyzer.
  1197.  * The lexer will return the same pointer for any identifier which
  1198.  * matches.
  1199.  */
  1200. AssignStrings ()
  1201. {
  1202.     fill_pstr = FindString ("f");
  1203.     hbox_pstr = FindString ("h");
  1204.     vbox_pstr = FindString ("v");
  1205.     tbox_pstr = FindString ("t");
  1206.     blok_pstr = FindString ("b");
  1207.     s_pstr = FindString ("s");
  1208.     p_pstr = FindString ("p");
  1209.     ph_pstr = FindString ("ph");
  1210.     pv_pstr = FindString ("pv");
  1211. }
  1212.  
  1213.  
  1214. /*
  1215.  * To read file: open, read base name, read optional default border and text
  1216.  * colors, read a box (a BIG box), read gadget info blocks, close.
  1217.  */
  1218. struct Box * ReadFile ()
  1219. {
  1220.     struct Box     *box;
  1221.     short           i, tok;
  1222.     char           *buf;
  1223.  
  1224.     AssignStrings ();
  1225.     tok = NextToken (&base);
  1226.     if (tok != RT_ID) {
  1227.         fprintf (stderr, "Cannot find base name\n");
  1228.         return NULL;
  1229.     }
  1230.     Qnum (&def_bcol, 10);
  1231.     Qnum (&def_tcol, 10);
  1232.  
  1233.     if (infoLevel > 1) printf ("base name: \"%s\"\ndefault border color:"
  1234.         " %d\ndefault text color: %d\n", base, def_bcol, def_tcol);
  1235.  
  1236.     box = ReadBox ();
  1237.     ReadGadInfo ();
  1238.  
  1239.     /*
  1240.      * Make sure we're at the end of the file to make the
  1241.      * lexer happy.  Print up to 10 error messages unless there
  1242.      * is no box from the previous call in which case there's something
  1243.      * wrong anyway.  (Unless in verbose mode, then show 'em all.)
  1244.      */
  1245.     i = ((box || infoLevel > 1) ? 10 : 0);
  1246.     while (NextToken (&buf) != RT_EOF) {
  1247.         if (i) {
  1248.             fprintf (stderr,
  1249.                 "Token found after end of data: <%s>\n", buf);
  1250.             if (!--i) fprintf (stderr, "... etc.\n");
  1251.         }
  1252.     }
  1253.  
  1254.     return box;
  1255. }
  1256.  
  1257.  
  1258. /*
  1259.  * ====  OUTPUT SECTION  ====
  1260.  *
  1261.  * Dumps structures created during the input and resolution phases of
  1262.  * the processing.  Just takes a pointer to a Requester in WriteRequester()
  1263.  * and dumps the related structures as well.
  1264.  */
  1265.  
  1266. /*
  1267.  * Write string info and buffer declarations from string gadgets
  1268.  * (if any).
  1269.  */
  1270. WriteStrGad (glist)
  1271.     struct Gadget        *glist;
  1272. {
  1273.     struct Gadget        *g;
  1274.     struct StringInfo    *si;
  1275.     int            i, n;
  1276.  
  1277.     /* Count number of string gadgets.
  1278.      */
  1279.     for (n = 0, g = glist; g; g = g->NextGadget)
  1280.         if (GTYPE(g) == STRGADGET) n++;
  1281.  
  1282.     if (!n) return;
  1283.  
  1284.     /* Write the necessary buffers for the string infos.
  1285.      */
  1286.     fprintf (file, "\n%sUBYTE %s_nbuf[%d][NUMCHR] = {\n\t",
  1287.         globStr, base, n);
  1288.     i = n;
  1289.     for (g = glist; g; g = g->NextGadget) {
  1290.         if (GTYPE(g) != STRGADGET) continue;
  1291.  
  1292.         si = (struct StringInfo *) g->SpecialInfo;
  1293.         fprintf (file, " \"%s\"", si->Buffer);
  1294.         if (--i) fprintf (file, ",");
  1295.     }
  1296.  
  1297.     fprintf (file, "\n};\n\n%sstruct StringInfo %s_sinfo[] = {\n",
  1298.         globStr, base);
  1299.     i = 0;
  1300.     for (g = glist; g; g = g->NextGadget) {
  1301.         if (GTYPE(g) != STRGADGET) continue;
  1302.  
  1303.         si = (struct StringInfo *) g->SpecialInfo;
  1304.         fprintf (file, "\t{&%s_nbuf[%d][0],undo,0,NUMCHR,0}",
  1305.             base, i++);
  1306.         if (--n) fprintf (file, ",");
  1307.         fprintf (file, "\n");
  1308.     }
  1309.     fprintf (file, "};\n");
  1310.  
  1311.     if (infoLevel > 1) printf ("wrote %d StringInfo structs\n", i);
  1312. }
  1313.  
  1314.  
  1315. /*
  1316.  * Write prop info and image declarations for prop gadgets (if any).
  1317.  */
  1318. WritePropGad (glist)
  1319.     struct Gadget        *glist;
  1320. {
  1321.     struct Gadget        *g;
  1322.     struct PropInfo        *pi;
  1323.     int            i, n;
  1324.  
  1325.     /* Count number of prop gadgets.
  1326.      */
  1327.     for (n = 0, g = glist; g; g = g->NextGadget)
  1328.         if (GTYPE(g) == PROPGADGET) n++;
  1329.  
  1330.     if (!n) return;
  1331.  
  1332.     /* Write the necessary images for the autoknobs.
  1333.      */
  1334.     fprintf (file, "\n%sstruct Image %s_pimg[%d];\n", globStr, base, n);
  1335.  
  1336.     /* Write the PropInfo structures themselves.
  1337.      */
  1338.     fprintf (file, "\n%sstruct PropInfo %s_pinfo[] = {\n", globStr, base);
  1339.     i = n;
  1340.     for (g = glist; g; g = g->NextGadget) {
  1341.         if (GTYPE(g) != PROPGADGET) continue;
  1342.  
  1343.         pi = (struct PropInfo *) g->SpecialInfo;
  1344.         fprintf (file, "\t{%u,%u,%u,%u,%u}", pi->Flags,
  1345.             pi->HorizPot, pi->VertPot,
  1346.             pi->HorizBody, pi->VertBody);
  1347.         if (--i) fprintf (file, ",");
  1348.         fprintf (file, "\n");
  1349.     }
  1350.     fprintf (file, "};\n");
  1351.  
  1352.     if (infoLevel > 1) printf ("wrote %d PropInfo structs\n", n);
  1353. }
  1354.  
  1355.  
  1356. /*
  1357.  * Write the gadgets from the main gadget list.  Returns number of
  1358.  * gadgets written.
  1359.  */
  1360. int WriteGadgets (glist)
  1361.     struct Gadget    *glist;
  1362. {
  1363.     struct Gadget    *g;
  1364.     int        k = 1, nimg=0, nprp=0, nstr=0;
  1365.     char        *nam;
  1366.  
  1367.     if (!glist) return 0;
  1368.  
  1369.     WriteStrGad (glist);
  1370.     WritePropGad (glist);
  1371.  
  1372.     fprintf (file, "\n%sstruct Gadget %s_gad[] = {\n", globStr, base);
  1373.  
  1374.     for (g = glist; g; g = g->NextGadget) {
  1375.         if (g->NextGadget)
  1376.             fprintf (file, "\t{&%s_gad[%d]", base, k++);
  1377.         else
  1378.             fprintf (file, "\t{NULL");
  1379.  
  1380.         fprintf (file, ",%d,%d,%d,%d,%u,%u,%u,", g->LeftEdge,
  1381.             g->TopEdge, g->Width, g->Height, g->Flags,
  1382.             g->Activation, g->GadgetType);
  1383.  
  1384.         if (GTYPE(g) == PROPGADGET)
  1385.             fprintf (file, "(APTR)&%s_pimg[%d]", base, nimg++);
  1386.         else
  1387.             fprintf (file, "NULL");
  1388.  
  1389.         fprintf (file, ",\n\t NULL,NULL,0,(APTR)");
  1390.  
  1391.         if (GTYPE(g) == PROPGADGET)
  1392.             fprintf (file, "&%s_pinfo[%d]", base, nprp++);
  1393.         else if (GTYPE(g) == STRGADGET)
  1394.             fprintf (file, "&%s_sinfo[%d]", base, nstr++);
  1395.         else
  1396.             fprintf (file, "NULL");
  1397.  
  1398.         if (nam = ((struct SuperGadget *) g)->gnam)
  1399.             fprintf (file, ",%s", nam);
  1400.         else
  1401.             fprintf (file, ",0x%x", g->GadgetID);
  1402.  
  1403.         if (g->NextGadget)
  1404.             fprintf (file, "},\n");
  1405.         else
  1406.             fprintf (file, "}\n");
  1407.     }
  1408.     fprintf (file, "};\n");
  1409.  
  1410.     if (infoLevel > 1) printf ("wrote %d Gadget structs\n", k);
  1411.     return k;
  1412. }
  1413.  
  1414.  
  1415. /*
  1416.  * Write out list of IntuiText structs for main list.  Returns number
  1417.  * of structures written.
  1418.  */
  1419. int WriteText (tlist)
  1420.     struct IntuiText    *tlist;
  1421. {
  1422.     struct IntuiText    *it;
  1423.     int            k = 1;
  1424.  
  1425.     if (!tlist) return 0;
  1426.  
  1427.     fprintf (file, "\n%sstruct IntuiText %s_txt[] = {\n", globStr, base);
  1428.  
  1429.     for (it = tlist; it; it = it->NextText) {
  1430.         fprintf (file, "\t{%d,%d,%d,%d,%d,&ta,(UBYTE*)\"%s\",",
  1431.             it->FrontPen, it->BackPen, it->DrawMode,
  1432.             it->LeftEdge, it->TopEdge, it->IText);
  1433.         if (it->NextText)
  1434.             fprintf (file, "&%s_txt[%d]},\n", base, k++);
  1435.         else
  1436.             fprintf (file, "NULL},\n");
  1437.     }
  1438.     fprintf (file, "};\n");
  1439.  
  1440.     if (infoLevel > 1) printf ("wrote %d IntuiText structs\n", k);
  1441.     return k;
  1442. }
  1443.  
  1444.  
  1445. /*
  1446.  * Write out list of XY arrays from Border struct main list 
  1447.  */
  1448. WriteBorderXY (lst)
  1449.     struct Border *lst;
  1450. {
  1451.     register struct Border *b;
  1452.     register short  i;
  1453.  
  1454.     fprintf (file, "\n%sshort %s_brd_XY[] = {\n", globStr, base);
  1455.     for (b = lst; b; b = b->NextBorder) {
  1456.         fprintf (file, "\t");
  1457.         for (i = 0; i < b->Count; i++) {
  1458.             fprintf (file, "%d,%d", b->XY[i * 2], b->XY[i * 2 + 1]);
  1459.             if (i != b->Count - 1 || b->NextBorder)
  1460.                 fprintf (file, ", ");
  1461.         }
  1462.         fprintf (file, "\n");
  1463.     }
  1464.     fprintf (file, "};\n");
  1465. }
  1466.  
  1467.  
  1468. /*
  1469.  * Write out list of Border structs from main list.  Returns nubmer of
  1470.  * structures written.
  1471.  */
  1472. int WriteBorder (lst)
  1473.     struct Border *lst;
  1474. {
  1475.     register struct Border *b;
  1476.     register short  i = 0, k = 1;
  1477.  
  1478.     if (!lst) return 0;
  1479.  
  1480.     WriteBorderXY (lst);
  1481.  
  1482.     fprintf (file, "\n%sstruct Border %s_brd[] = {\n", globStr, base);
  1483.     for (b = lst; b; b = b->NextBorder) {
  1484.         fprintf (file, "\t{0,0,%d,0,JAM1,%d,&%s_brd_XY[%d],",
  1485.              b->FrontPen, b->Count, base, i);
  1486.         i += b->Count * 2;
  1487.         if (b->NextBorder)
  1488.             fprintf (file, "&%s_brd[%d]},\n", base, k++);
  1489.         else
  1490.             fprintf (file, "NULL}\n");
  1491.     }
  1492.     fprintf (file, "};\n");
  1493.  
  1494.     if (infoLevel > 1) printf ("wrote %d Border structs\n", k);
  1495.     return k;
  1496. }
  1497.  
  1498.  
  1499. /*
  1500.  * Reverse the gadget list so it will make more sense to the client.
  1501.  * This way they will appear in the arrays in the order that they 
  1502.  * appear in the description file.
  1503.  */
  1504. struct Gadget * ReverseGadList (head)
  1505.     struct Gadget *head;
  1506. {
  1507.     struct Gadget *newhead = NULL, *nxt;
  1508.  
  1509.     for (; head; head = nxt) {
  1510.         nxt = head->NextGadget;
  1511.         head->NextGadget = newhead;
  1512.         newhead = head;
  1513.     }
  1514.     return newhead;
  1515. }
  1516.  
  1517.  
  1518. /*
  1519.  * The main output function.
  1520.  */
  1521. WriteRequester (name, req)
  1522.     char             *name;
  1523.     struct Requester *req;
  1524. {
  1525.     short           i, ng, nt, nb;
  1526.  
  1527.     if (!(file = fopen (name, "w"))) {
  1528.         fprintf (stderr, "Can't open output file\n");
  1529.         return;
  1530.     }
  1531.  
  1532.     req->ReqGadget = ReverseGadList (req->ReqGadget);
  1533.     ng = WriteGadgets (req->ReqGadget);
  1534.     nt = WriteText (req->ReqText);
  1535.     nb = WriteBorder (req->ReqBorder);
  1536.  
  1537.     /*
  1538.      * The requester itself.
  1539.      */
  1540.     fprintf (file, "\n%sstruct Requester %s_req = {\n\
  1541. \tNULL,0,0,%d,%d,0,0,", globStr, base, req->Width, req->Height);
  1542.     if (ng) fprintf (file, "%s_gad,", base);
  1543.     else    fprintf (file, "NULL,");
  1544.     if (nb) fprintf (file, "%s_brd,", base);
  1545.     else    fprintf (file, "NULL,");
  1546.     if (nt) fprintf (file, "%s_txt,", base);
  1547.     else    fprintf (file, "NULL,");
  1548.     fprintf (file, "0,0,\n\tNULL,{NULL},NULL,NULL,{NULL}\n};\n");
  1549.  
  1550.     fclose (file);
  1551. }
  1552.  
  1553.  
  1554. MemError ()
  1555. {
  1556.     fprintf (stderr, "Out of memory.\n");
  1557. }
  1558.  
  1559.  
  1560. /* Main entry point.  Decode args and call body function.  Args are:
  1561.  *
  1562.  *    -p    : Print box description
  1563.  *    -q    : Run silent, run deep
  1564.  *    -v    : Verbose (not much different than normal, really)
  1565.  *    -d    : Display preview (is default unless output requested)
  1566.  *    -s    : Send Requester declarations to stdout
  1567.  */
  1568. main (argc, argv)
  1569.     int             argc;
  1570.     char           *argv[];
  1571. {
  1572.     int    i, junk = 0, prev = 0, tostdout = 0;
  1573.     char    *infile = NULL, *outfile = NULL;
  1574.  
  1575.     /*
  1576.      * Decode arguments.
  1577.      */
  1578.     for (i = 1; i < argc; i++) {
  1579.         if (argv[i][0] == '-') {
  1580.             switch (argv[i][1]) {
  1581.                 case 'p':
  1582.                 printBoxes = 1;
  1583.                 break;
  1584.                 case 'q':
  1585.                 infoLevel = 0;
  1586.                 break;
  1587.                 case 'v':
  1588.                 infoLevel = 2;
  1589.                 break;
  1590.                 case 'd':
  1591.                 prev = 1;
  1592.                 break;
  1593.                 case 's':
  1594.                 tostdout = 1;
  1595.                 break;
  1596.                 case 'g':
  1597.                 globStr = "";
  1598.                 break;
  1599.                 default:
  1600.                 junk = 1;
  1601.             }
  1602.         } else {
  1603.             if (!infile) infile = argv[i];
  1604.               else if (!outfile) outfile = argv[i];
  1605.               else junk = 1;
  1606.         }
  1607.     }
  1608.     if (junk || !infile) {
  1609.         printf ("Usage: %s [-p|q|v|d|s|g] <file> [<outfile>]\n",
  1610.             argv[0]);
  1611.         exit (1);
  1612.     }
  1613.     if (tostdout) {
  1614.         outfile = "*";
  1615.         infoLevel = 0;
  1616.     }
  1617.     showPreview = (!outfile || prev);
  1618.  
  1619.     if (IntuitionBase = (struct IntuitionBase *)
  1620.         OpenLibrary ("intuition.library", 0L)) {
  1621.         Body (infile, outfile);
  1622.         CloseLibrary (IntuitionBase);
  1623.     }
  1624. }
  1625.  
  1626.  
  1627. Body (infile, outfile)
  1628.     char           *infile, *outfile;
  1629. {
  1630.     struct Window  *win;
  1631.     struct Box     *b;
  1632.     short           h, w;
  1633.  
  1634.     if (infoLevel > 0) printf (
  1635.         "Requester generator v2   Jan 1989  Stuart Ferguson\n");
  1636.     if (!OpenLexFile (infile)) {
  1637.         fprintf (stderr, "Cannot open %s\n", infile);
  1638.         return;
  1639.     }
  1640.     if (b = ReadFile ()) {
  1641.         Format (b);
  1642.         b->x = b->y = 0;
  1643.         Layout (b, b->xs, b->ys);
  1644.         if (printBoxes) PrintBox (b, 0);
  1645.  
  1646.         if (CreateIText (b)) {
  1647.             if (CreateBorder (b)) {
  1648.                 MergeBorders ();
  1649.                 MergeLinear ();
  1650.                 PositionGadgets ();
  1651.  
  1652.                 mreq.Width = b->xs;
  1653.                 mreq.Height = b->ys;
  1654.                 mreq.ReqGadget = glst;
  1655.                 mreq.ReqText = itlst;
  1656.                 mreq.ReqBorder = blst;
  1657.  
  1658.                 if (showPreview) PreviewRequester (&mreq);
  1659.                 if (outfile) WriteRequester (outfile, &mreq);
  1660.  
  1661.                 FreeBorder ();
  1662.             }
  1663.             FreeIText ();
  1664.         }
  1665.         FreeBox (b);
  1666.     } else fprintf (stderr, "Error reading box description.\n");
  1667.     LexCleanup ();
  1668. }
  1669.  
  1670.  
  1671. /*
  1672.  * Open a window to preview the requester layout.
  1673.  */
  1674. PreviewRequester (req)
  1675.     struct Requester *req;
  1676. {
  1677.     struct Window  *win;
  1678.     short           h, w;
  1679.  
  1680.     w = req->Width + 12;
  1681.     h = req->Height + 16;
  1682.     if (w > 640 || h > 200) {
  1683.         fprintf (stderr, "Requester too large for preview.\n");
  1684.         return;
  1685.     }
  1686.     if (w < 150) w = 150;
  1687.     if (h < 60) h = 60;
  1688.  
  1689.     nwin.Width = w;
  1690.     nwin.Height = h;
  1691.     if (!(win = OpenWindow (&nwin))) {
  1692.         fprintf (stderr, "Unable to open preview window.\n");
  1693.         return;
  1694.     }
  1695.  
  1696.     req->LeftEdge =
  1697.         (w - win->BorderRight + win->BorderLeft - req->Width) / 2;
  1698.     req->TopEdge =
  1699.         (h - win->BorderBottom + win->BorderTop - req->Height) / 2;
  1700.  
  1701.     RequesterLoop (win, req);
  1702.     CloseWindow (win);
  1703. }
  1704.  
  1705.  
  1706. RequesterLoop (win, req)
  1707.     struct Window *win;
  1708.     struct Requester *req;
  1709. {
  1710.     struct Gadget *g;
  1711.     struct IntuiMessage *im;
  1712.     ULONG class, oldflags;
  1713.     USHORT code;
  1714.     int gend = 0, looping;
  1715.  
  1716.     /*
  1717.      * Determine if this requester can be terminated with a gadget.
  1718.      * If not, provide an alternate exit facility.
  1719.      */
  1720.     for (g = req->ReqGadget; g; g = g->NextGadget)
  1721.         if (g->Activation & ENDGADGET) {
  1722.             gend = 1;
  1723.             break;
  1724.         }
  1725.     oldflags = req->Flags;
  1726.     if (!gend) {
  1727.         if (infoLevel > 0) printf (
  1728.             "No Endgadget -- Press ESC to exit Requester.\n");
  1729.         req->Flags |= NOISYREQ;
  1730.     }
  1731.  
  1732.     if (!Request (req, win)) {
  1733.         fprintf (stderr, "Unable to post Requester.\n");
  1734.         req->Flags = oldflags;
  1735.         return;
  1736.     }
  1737.  
  1738.     looping = 1;
  1739.     while (looping) {
  1740.         im = (struct IntuiMessage *) GetMsg(win->UserPort);
  1741.         if (!im) {
  1742.             WaitPort (win->UserPort);
  1743.             continue;
  1744.         }
  1745.         class = im->Class;
  1746.         code = im->Code;
  1747.         ReplyMsg (im);
  1748.         if (class == VANILLAKEY && code == 27) break;
  1749.         if (infoLevel > 0) printf ("Message : %s\n", IMClass (class));
  1750.         if (class == REQCLEAR) looping = 0;
  1751.     }
  1752.     if (looping) EndRequest (req, win);
  1753.     req->Flags = oldflags;
  1754. }
  1755.  
  1756.  
  1757. /*
  1758.  * Returns name of message class.  Lots more classes here than are
  1759.  * possible, but what the hell.
  1760.  */
  1761. char * IMClass (class)
  1762.     ULONG class;
  1763. {
  1764.     switch (class) {
  1765.         case SIZEVERIFY:    return ("SIZEVERIFY");
  1766.         case NEWSIZE:    return ("NEWSIZE");
  1767.         case REFRESHWINDOW:    return ("REFRESHWINDOW");
  1768.         case MOUSEBUTTONS:    return ("MOUSEBUTTONS");
  1769.         case MOUSEMOVE:    return ("MOUSEMOVE");
  1770.         case GADGETDOWN:    return ("GADGETDOWN");
  1771.         case GADGETUP:    return ("GADGETUP");
  1772.         case REQSET:    return ("REQSET");
  1773.         case MENUPICK:    return ("MENUPICK");
  1774.         case CLOSEWINDOW:    return ("CLOSEWINDOW");
  1775.         case RAWKEY:    return ("RAWKEY");
  1776.         case REQVERIFY:    return ("REQVERIFY");
  1777.         case REQCLEAR:    return ("REQCLEAR");
  1778.         case MENUVERIFY:    return ("MENUVERIFY");
  1779.         case NEWPREFS:    return ("NEWPREFS");
  1780.         case DISKINSERTED:    return ("DISKINSERTED");
  1781.         case DISKREMOVED:    return ("DISKREMOVED");
  1782.         case WBENCHMESSAGE:    return ("WBENCHMESSAGE");
  1783.         case ACTIVEWINDOW:    return ("ACTIVEWINDOW");
  1784.         case INACTIVEWINDOW:    return ("INACTIVEWINDOW");
  1785.         case DELTAMOVE:    return ("DELTAMOVE");
  1786.         case VANILLAKEY:    return ("VANILLAKEY");
  1787.         case INTUITICKS:    return ("INTUITICKS");
  1788.     }
  1789. }
  1790.  
  1791.  
  1792. #define DEBUG
  1793.  
  1794. /*
  1795.  * Debug routines.
  1796.  */
  1797. #ifdef DEBUG
  1798.  
  1799. PrintBorder ()
  1800. {
  1801.     struct Border *b;
  1802.     short i;
  1803.  
  1804.     printf ("Borders:\n");
  1805.     for (b=blst; b; b=b->NextBorder) {
  1806.         printf ("%d %d %d %d\n:: ", b->LeftEdge,
  1807.             b->TopEdge, b->FrontPen, b->Count);
  1808.         for (i=0; i<b->Count; i++)
  1809.             printf ("%d,%d ", b->XY[i*2],b->XY[i*2+1]);
  1810.         printf ("\n");
  1811.     }
  1812. }
  1813.  
  1814.  
  1815. PrintGadget ()
  1816. {
  1817.     USHORT        typ;
  1818.     struct Gadget    *g;
  1819.  
  1820.     printf ("Gadgets:\n");
  1821.     for (g=glst; g; g=g->NextGadget) {
  1822.         printf ("%d,%d %d,%d  ", g->LeftEdge, g->TopEdge,
  1823.             g->Width, g->Height);
  1824.         typ = GTYPE(g);
  1825.         if (typ == PROPGADGET)    printf ("PROP");
  1826.         if (typ == STRGADGET)    printf ("STRING");
  1827.         if (typ == BOOLGADGET)    printf ("BOOL");
  1828.         printf ("\n");
  1829.     }
  1830. }
  1831. #endif
  1832. SHAR_EOF
  1833. cat << \SHAR_EOF > blk.doc
  1834.  
  1835.  
  1836.         blk - Automatic Requester Generator
  1837.             Stuart Ferguson
  1838.  
  1839.  
  1840. Blk generates C code for declaring requesters.  It automatically formats
  1841. borders and text and generates the declarations for them.  The requester
  1842. can contain boolean, string and proportional gadgets.
  1843.  
  1844.  
  1845. Boxes:
  1846.  
  1847. A Requester and all its internal structures are described to blk as a
  1848. tree of nested boxes.  The root box is the Requester itself.  Inner
  1849. boxes can be stacked horizontally or vertically or be within each
  1850. other.  Borders and text strings are also boxes for the purpose of
  1851. formatting.  The box grammer, which is loosly based on lisp syntax, can
  1852. be described by the BNF-like description given below.  (Non-terminal
  1853. symbols are named and terminal symbols are double quoted strings.)
  1854.  
  1855.     box   :== fill | text | bord | block | seq
  1856.     fill  :== "f"
  1857.     text  :== "(t" onum str ")"
  1858.     str   :== [double quoted string]
  1859.     bord  :== rule | "(" rule num onum ")"
  1860.     rule  :== "|" | "-"
  1861.     block :== "(b" num num ")"
  1862.     seq   :== "(" ("h"|"v") blist ")"
  1863.     blist :== box | box blist
  1864.     onum  :== num | [nothing]
  1865.     num   :== [unsigned integer]
  1866.  
  1867.  
  1868. Some examples:
  1869.  
  1870. The following box description would produce a 10 x 10 pixel area with
  1871. borders on all four sides:
  1872.  
  1873.     (h | (v - (b 10 10) - ) | )
  1874.  
  1875. The central structure, "(b 10 10)," is a 10 x 10 block.  This is one of
  1876. three boxes in a vbox, or vertically oriented list of boxes.  The other
  1877. two boxes in the list are hrules, the "-"'s, which are horizontal "rules,"
  1878. or lines.  This produces a box containing an empty area with lines above
  1879. and below it.  This box is further included with two vertical rules, "|,"
  1880. in an hbox - a horizontally oriented list.  This puts horizontal lines
  1881. on each side of the box, closing the square.
  1882.  
  1883.       -----       |-----|
  1884.       xxxxx       |xxxxx|
  1885.       xxxxx       |xxxxx|
  1886.       xxxxx       |xxxxx|
  1887.       -----       |-----|
  1888.  
  1889. Rules are elastic, and while they have no intrinsic length of their own,
  1890. they will expand to fill any space available within their bounding box.
  1891. The vbox above:
  1892.  
  1893.     (v - (b 10 10) - )
  1894.  
  1895. has dimensions 10 x 12, so each horizontal rule expands to a length of
  1896. 10 to fill the enclosing box.
  1897.  
  1898. Fills are another type of elastic object.  For example, suppose you wanted
  1899. to format a set of strings, like:
  1900.  
  1901.     Line #1
  1902.     Blah Blah
  1903.     ETC
  1904.  
  1905. You could list the text boxes in a vbox, thus:
  1906.  
  1907.     (v (t "Line #1") (t "Blah Blah") (t "ETC"))
  1908.  
  1909. This would produce the formated lines as above.  But then suppose you wanted
  1910. each line centered within its box.  For this you use fills.  Each fill
  1911. expands to take up as much free space as it can within its bounding box,
  1912. and if a string is sandwiched between two fills horizontally, it will be
  1913. squeezed by competing fills into the center of its bounding box.
  1914.  
  1915.     (v
  1916.        (h f (t "Line #1")   f)
  1917.        (h f (t "Blah Blah") f)
  1918.        (h f (t "ETC")       f)
  1919.     )
  1920.  
  1921. Now the boxes within the outer vbox are not text boxes but hboxes
  1922. containing text boxes and fills to the left and right.  The width of the
  1923. vbox is computed as the maximum width of each box it contains.  Since
  1924. fills have no intrinsic size, the widest hbox is the one containing the
  1925. text box (t"Blah Blah"), which has a width of 9 characters (blk always 
  1926. assumes a fixed size font).  If the characters are 8 pixels wide, the
  1927. largest hbox will be 72 pixels.  Each of the other two shorter hboxes
  1928. are contained within a vbox also of width 72, so each fill expands to
  1929. make up the difference.  For example, the first hbox contains two fills
  1930. and a 56 pixel wide text box.  To fill up a 72 pixel wide bounding box,
  1931. each fill can expand 8 pixels horizontally, thus centering the text box
  1932. inbetween them.  The result is:
  1933.  
  1934.      Line #1
  1935.     Blah Blah
  1936.        ETC
  1937.  
  1938. This whole structure is just a box, and can be used within other box
  1939. structures.  For example, to put a border around the above box, you could
  1940. use the definition from the first example.  Specifically:
  1941.  
  1942.  (h | (v - 
  1943.    (v
  1944.       (h f (t"Line #1")   f)
  1945.       (h f (t"Blah Blah") f)
  1946.       (h f (t"ETC")       f)
  1947.     )
  1948.   -) |)
  1949.  
  1950. The problem with this (you will see if you try it) is that the rules
  1951. suround the text too closely.  Since there is no space between the text and
  1952. the lines they lie almost right on top of each other.  Text boxes look fine
  1953. right next to each other, but rules need to be explicitly spaced away from
  1954. text.  This can be done with "struts," blocks used explicitly as spacers,
  1955. like so:
  1956.  
  1957.     (h | (v - (b 0 5) (h (b 5 0)
  1958.        (v
  1959.           (h f (t"Line #1")   f)
  1960.           (h f (t"Blah Blah") f)
  1961.           (h f (t"ETC")       f)
  1962.        )
  1963.      (b 5 0)) (b 0 5) -) |)
  1964.  
  1965. The 0 x 5 and 5 x 0 blocks have no volume but since they have width or
  1966. height, they affect the spacing of the other components in their h- or
  1967. vboxes.
  1968.  
  1969. Text and rules take on the default color (see "blk files" below) unless the
  1970. color is explicitly stated.  For text boxes this is done with the optional
  1971. parameter in the text box description, as in (t 3 "hi there"), which
  1972. specifies that the text be color #3.  For rules, a different description
  1973. format is used.  Instead of simply | or -, a form such as (| 1 3) or (- 1 2)
  1974. is used.  The two examples create a vrule of color 3 and an hrule of color 2
  1975. respectively.  The first number is the width of the rule and should always
  1976. be 1.
  1977.  
  1978. With these simple tools, it is possible to design a wide variety of nicely
  1979. layed-out requesters without dealing with a single coordinate or C struct.  
  1980. What is more, the strings in the above box definition could be changed to 
  1981. anything, and the result would still be three strings centered within a 
  1982. perfectly sized box.
  1983.  
  1984. Blk also supports a 'C'-style preprocesser for include files, macros,
  1985. comments and "ifdef's".  Macros can make deeply nested box structures
  1986. more readable and consistent.
  1987.  
  1988.  
  1989. Gadgets:
  1990.  
  1991. Any sub-box in a box definition can be defined to be a gadget hit box by
  1992. following it with the sequence:
  1993.  
  1994.    ":" num
  1995.  
  1996. From the above example, each text box could be defined as a hit box for a
  1997. gadget by restating it as:
  1998.  
  1999.       (h f (t"Line #1"):1   f)
  2000.       (h f (t"Blah Blah"):2 f)
  2001.       (h f (t"ETC"):3       f)
  2002.  
  2003. Given these box definitions, blk will generate code for 3 BOOLEAN,
  2004. RELVERIFY, REQGADGET, GADGHCOMP gadgets with the GadgetID's 1,2 and 3.
  2005. Other types of gadgets can be defined using an extended gadget definition.
  2006. These are of the form:
  2007.  
  2008.    num {"s"|"p"|"pv"|"ph"} {":"id} flags
  2009.  
  2010. Stuff in {}'s is optional; "id" and "flags" are double-quoted strings.
  2011. "Num" is the id number for the gadget given after the ":" in the box
  2012. definition.  The optional "s", "p", "pv" and "ph" specify string or 
  2013. proportional gadgets, the default being boolean.  "ph" is a FREEHORIZ
  2014. proportional gadget, "pv" is FREEVERT, and "p" is both.  The optional 
  2015. "id" string specifies a string to be used for the GadgetID field in the
  2016. gadget struct instead of just using "0x<num>," where num is the id 
  2017. given in the box description.  "Flags" is a set of characters used to 
  2018. specify flags available in defining a gadget.  Valid flags are (case 
  2019. is significant):
  2020.  
  2021.       B : GADGHBOX
  2022.       t : TOGGLESELECT
  2023.       v : RELVERIFY
  2024.       e : ENDGADGET
  2025.       i : GADGIMMEDIATE
  2026.       c : STRINGCENTER  (string gadgets only)
  2027.       f : FOLLOWMOUSE
  2028.  
  2029. Some examples:
  2030.  
  2031. 1ph""             - horizontally moving proportional gadget with no flags
  2032. 7:"OK_ID""ev"     - boolean gadget with id "OK_ID"
  2033.                      RELVERIFY and ENDGADGET flags set
  2034. 5s"v"             - RELVERIFY string gadget
  2035. 5s:"STR_ID""i"    - string gadget with id "STR_ID", GADGIMMEDIATE flag
  2036.  
  2037.  
  2038. Blk output:
  2039.  
  2040. The code that blk outputs follows some simple conventions.  These were
  2041. implemented for my own convenience and can be changed by altering the source
  2042. code.  Structs are assigned in arrays, the name of each array being some
  2043. base name followed by the type of struct.  For example, if the base name
  2044. were "xyz," some structs would be "xyz_req," "xyz_str," etc.  They are:
  2045.  
  2046.     struct Requester <base>_req      /* not an array */
  2047.  
  2048.     struct Gadget <base>_gad[]
  2049.     struct IntuiText <base>_txt[]
  2050.     struct Border <base>_brd[]
  2051.     short <base>_brd_XY[]
  2052.  
  2053.     UBYTE <base>_nbuf[][NUMCHR]
  2054.     struct StringInfo <base>_sinfo[]
  2055.  
  2056.     struct Image <base>_pimg[]
  2057.     struct PropInfo <base>_pinfo[]
  2058.  
  2059. Some of these may be ommited if there are no data of that type.
  2060.  
  2061. In user code, the symbol "ta" must be declared as struct TextAttr as in:
  2062.  
  2063.     struct TextAttr ta = { ... };
  2064. or
  2065.     extern struct TextAttr ta;
  2066.  
  2067. To use string gadgets, the symbol "NUMCHR" must be #define'd as the number
  2068. of characters in the string buffer.  The symbol "undo" must be declared as
  2069.  
  2070.     UBYTE undo[NUMCHR];
  2071.  
  2072. to be used as the undo buffer for string gadgets (or #define'd as NULL).  
  2073. The string buffers will be assigned in the order that they are encountered
  2074. in reading the box description, so the buffer for the first encountered 
  2075. will be <base>_nbuf[0], the buffer for the second will be <base>_nbuf[1], 
  2076. and so on.
  2077.  
  2078.  
  2079. Blk files:
  2080.  
  2081. A blk input file contains all the information to describe a single
  2082. requester.  It starts with a string that is to be the base name for this set
  2083. of declarations.  There are then two optional numbers which are the default
  2084. border color and default text color.  Neither need to be specified and will
  2085. both default to 1.  Then comes the box definition followed by a set of
  2086. extended gadget descriptions.  Some examples are included in this release.
  2087.  
  2088.  
  2089. Using blk:
  2090.  
  2091. The blk commandline is:
  2092.  
  2093.     blk [-p|q|v|s|d|g] <infile> [<outfile>]
  2094.  
  2095.     <infile>    - Input description file -- required.
  2096.     <outfile>    - Output 'C' header file.
  2097.     -p        - Print box description.
  2098.     -s        - Output 'C' header file to stdout.
  2099.     -d        - Display a preview Requester.
  2100.     -g        - Write global declarations.
  2101.     -q        - Run quiet.
  2102.     -v        - Run verbose.
  2103.  
  2104. "Infile" is the input requester description.  If no output is specified,
  2105. the resulting Requester will be displayed in a window.  If output is
  2106. specified, blk will not normally display a preview, but it will if the
  2107. "-d" option is specified.  The "-p" option prints a description of the
  2108. formatted box tree structure to stdout.  This can be a huge ammout of 
  2109. output, but can be useful if you need to know the exact coordinates or 
  2110. size of something.  "-g" will write the 'C' declrations as global; the
  2111. default is "static".  'C' output can be redirected to stdout with the
  2112. "-s" option.  If this option is used, "-q" is also assumed.
  2113.  
  2114. The preview Requester will work and can be played with.  Blk will display
  2115. the classes of the messages it gets from Intuition as you play with the
  2116. Gadgets in the Requester.  If the Requester itself has no "ENDGADGET"
  2117. type gadgets for terminating the Requester, blk allows you to end the
  2118. preview session by pressing ESC when the preview window is active.
  2119.  
  2120.  
  2121. Caveats:
  2122.  
  2123. The program is massivly recursive.  Be sure that you have enough stack
  2124. to deal with the box description you give.  The deeper you nest your
  2125. box descriptions, the deeper blk will go in interpreting them.  I keep
  2126. my stack at 12000 and don't have any problems with it.
  2127.  
  2128. Blk is intended in the same spirit as a compiler, so the 'C' output 
  2129. declarations are not designed to be directly editable.  For example, 
  2130. the Gadget flags are not described symbolically, but rather are dumped 
  2131. numerically into the declared structures.  The answer to problems
  2132. with this is to modify the blk source.
  2133.  
  2134. If you do decide to hack the source, be so kind as to keep me informed
  2135. of any useful improvements you make in case I decide to do yet another
  2136. version.
  2137.  
  2138.     Stuart Ferguson        1/89
  2139.     (shf@well.UUCP)
  2140.  
  2141.     123 James Ave.
  2142.     Redwood City, Ca.
  2143.         94062
  2144. SHAR_EOF
  2145. cat << \SHAR_EOF > colors.r
  2146. #include "macros.r"
  2147.  
  2148. /* Button with text and some space for borders for racks of buttons.
  2149.  * Button is hbox that will fill horizontally with text centered.
  2150.  */
  2151. #define TBUTTON(txt,num) \
  2152.     (h f (b 5 0) (v (b 0 4) (t txt) (b 0 3)) (b 5 0) f):num
  2153.  
  2154. col 1 1
  2155.  
  2156. START_REQ
  2157.     TITLE ("Default Color")
  2158.     (b 0 5)
  2159.     (h f |
  2160.       (v - TBUTTON ("Matte",1) - TBUTTON ("Glossy",2)     -) |
  2161.       (v - TBUTTON ("Unshaded",3) - TBUTTON ("Outline",4) -)
  2162.      | f)
  2163.     (b 0 4)
  2164.     (h f |
  2165.        (v - TBUTTON ("Transparent",10) -) |
  2166.        (v - TBUTTON ("Opaque",11)      -)
  2167.      | f)
  2168.     (b 0 4)
  2169.     (h f
  2170.        (v f(t 2"Smoothing:")f)
  2171.        (b 8 0)
  2172.        (v -(h |TBUTTON ("On",12)|TBUTTON ("Off",13)|)-)
  2173.        f
  2174.     )
  2175.     (b 0 5)
  2176.     (h f (v f (t"+"):5 f)
  2177.        (b 15 0)
  2178.        | (v (-1) (b 0 1) (h(b 5 0) (t"15 "):6 (b 5 0)) (-1)) |
  2179.        (b 15 0)
  2180.        (v f (t"-"):7 f) f)
  2181.     (b 0 5)
  2182.     OKCAN(8,9)
  2183. END_REQ
  2184.  
  2185.  
  2186. 1 "tv" 2 "tv" 3 "tv" 4 "tv"
  2187. 10 "tv" 11 "tv"  12 "tv" 13 "tv"
  2188. 6s"v"
  2189.  
  2190. OKCAN_EXT(8,9)
  2191. SHAR_EOF
  2192. cat << \SHAR_EOF > lex.c
  2193. /*
  2194.  * Lexical Analysis Functions Takes input stream and returns "tokens".
  2195.  * Implements pre-processor, allowing #include's and #define's tokens are
  2196.  * character strings.
  2197.  */
  2198. #include <stdio.h>
  2199. #include <ctype.h>
  2200. #include <functions.h>
  2201. #include <exec/types.h>
  2202. #include "listmac.h"
  2203. #include "std.h"
  2204. #include "lex.h"
  2205.  
  2206.  
  2207. struct StringElt {
  2208.     struct Nod      node;
  2209.     char           *buf;
  2210. };
  2211.  
  2212. struct Token {
  2213.     struct Nod      node;
  2214.     char           *buf;
  2215.     short           type, can_be_macro;
  2216.     char            chr;
  2217. };
  2218.  
  2219. struct Macro {
  2220.     struct Nod      node;
  2221.     struct Lst      arguments;
  2222.     struct Lst      tokens;
  2223.     char           *name;
  2224.     short           active, nargs;
  2225. };
  2226.  
  2227. struct ActiveMacro {
  2228.     struct Nod      node;
  2229.     struct Macro   *mac;
  2230.     struct Token   *curtok;
  2231. };
  2232.  
  2233. struct LexFile {
  2234.     struct Nod      node;
  2235.     FILE           *fp;
  2236.     short           newln;
  2237.     short           nextchar;
  2238. };
  2239.  
  2240. struct IfBlock {
  2241.     struct Nod     *node;
  2242.     short           skip_else;
  2243. };
  2244.  
  2245.  
  2246. struct Token   *CopyToken ();
  2247. struct Macro   *FindMacro ();
  2248. char           *FindString ();
  2249.  
  2250.  
  2251. #define BUFLEN 100
  2252. static char     buf[BUFLEN];
  2253. static short    bufpos;
  2254. static struct Token
  2255.         rawtok, *lasttok, *retok;
  2256.  
  2257. static struct LexFile *toplf;
  2258. static struct Lst macros, activemacs, files, ifblocks;
  2259. static short    files_open = 0, init_done = 0;
  2260. static char    *includestr, *definestr, *ifdefstr,
  2261.         *ifndefstr, *elsestr, *endifstr;
  2262.  
  2263. static char    *libbase = "hd:include/", libnam[80], basnam[80];
  2264.  
  2265. #define NHASH 53
  2266. struct Lst      hashTab[NHASH];
  2267.  
  2268.  
  2269. /*
  2270.  * Open a file for lexing - could be an include.
  2271.  * Set newline flag to true.
  2272.  */
  2273. short OpenLexFile (name)
  2274.     char           *name;
  2275. {
  2276.     struct LexFile *lf;
  2277.     struct Macro   *mac;
  2278.  
  2279.     /*
  2280.      * on first call, init environment vars 
  2281.      */
  2282.     if (!init_done) {
  2283.         NewList (&files);
  2284.         NewList (¯os);
  2285.         NewList (&ifblocks);
  2286.         NewList (&activemacs);
  2287.         InitHash ();
  2288.         includestr = FindString ("include");
  2289.         definestr = FindString ("define");
  2290.         ifdefstr = FindString ("ifdef");
  2291.         ifndefstr = FindString ("ifndef");
  2292.         elsestr = FindString ("else");
  2293.         endifstr = FindString ("endif");
  2294.         init_done = 1;
  2295.     }
  2296.  
  2297.     /*
  2298.      * if there are no files currently open, prepare for a new one 
  2299.      */
  2300.     if (!files_open) {
  2301.         while (mac = (struct Macro *) RemHead (¯os))
  2302.             FreeMacro (mac);
  2303.         ASSERT (!TEST (HEAD (&ifblocks)));
  2304.         ASSERT (!TEST (HEAD (&activemacs)));
  2305.         retok = NULL;
  2306.     }
  2307.     lf = NEW (struct LexFile);
  2308.     if (!lf) return 0;
  2309.  
  2310.     /*
  2311.      * printf ("opening \"%s\"\n", name); 
  2312.      */
  2313.     lf->fp = fopen (name, "r");
  2314.     if (!lf->fp) {
  2315.         FREI (lf);
  2316.         return 0;
  2317.     }
  2318.     lf->newln = 1;
  2319.     lf->nextchar = getc (lf->fp);
  2320.     AddHead (&files, lf);
  2321.     toplf = lf;
  2322.  
  2323.     if (feof (lf->fp)) {
  2324.         CloseLexFile ();
  2325.         return 0;
  2326.     }
  2327.     files_open = 1;
  2328.     return 1;
  2329. }
  2330.  
  2331.  
  2332. /*
  2333.  * Close top file returning 1 for the last file now closed.
  2334.  */
  2335. short CloseLexFile ()
  2336. {
  2337.     if (!files_open) return 1;
  2338.  
  2339.     fclose (toplf->fp);
  2340.     Remove (toplf);
  2341.     FREI (toplf);
  2342.     toplf = HEAD (&files);
  2343.     if (TEST (toplf)) return 0;
  2344.     files_open = 0;
  2345.     toplf = 0;
  2346.     return 1;
  2347. }
  2348.  
  2349.  
  2350. /*
  2351.  * free stuff up 
  2352.  */
  2353. LexCleanup ()
  2354. {
  2355.     struct Macro   *mac, *nmac;
  2356.  
  2357.     while (files_open) CloseLexFile ();
  2358.  
  2359.     FreeHash ();
  2360.     for (mac = HEAD (¯os); nmac = NEXT (mac); mac = nmac)
  2361.         FreeMacro (mac);
  2362.     ASSERT (!TEST (HEAD (&activemacs)));
  2363.  
  2364.     /*
  2365.      * initialization can be done again 
  2366.      */
  2367.     init_done = 0;
  2368. }
  2369.  
  2370.  
  2371. FreeMacro (mac)
  2372.     struct Macro   *mac;
  2373. {
  2374.     struct Token   *tok;
  2375.     struct StringElt *se;
  2376.  
  2377.     while (tok = (struct Token *) RemHead (&mac->tokens))
  2378.         FREI (tok);
  2379.     while (se = (struct StringElt *) RemHead (&mac->arguments))
  2380.         FREI (se);
  2381.     FREI (mac);
  2382. }
  2383.  
  2384.  
  2385. /*
  2386.  * Get next raw character from file dealing with backslash escape chars.
  2387.  * This is kind of wrong since these really ought to be recognized only
  2388.  * inside strings.  Doing it this way deals with backslashes before
  2389.  * newlines.
  2390.  */
  2391. short NextC1 (fp)
  2392.     FILE           *fp;
  2393. {
  2394.     short           c, k, radix;
  2395.  
  2396.     c = getc (fp);
  2397.     if (c == '\\') {
  2398.         c = getc (fp);
  2399.         if (isdigit (c)) {
  2400.             radix = 10;
  2401.             if (c == '0') radix = 8;
  2402.             k = (c - '0');
  2403.             while (isdigit (c = getc (fp)))
  2404.                 k = k * radix + (c - '0');
  2405.             ungetc (c, fp);
  2406.             c = k;
  2407.         } else {
  2408.             switch (c) {
  2409.                 case '\\':
  2410.                 break;
  2411.                 case '"':
  2412.                 c = '.';
  2413.                 break;
  2414.                 case 'n':
  2415.                 c = '\n';
  2416.                 break;
  2417.                 case 'b':
  2418.                 c = '\b';
  2419.                 break;
  2420.                 case 't':
  2421.                 c = '\t';
  2422.                 break;
  2423.                 case '\n':
  2424.                 c = getc (fp);
  2425.             }
  2426.         }
  2427.     }
  2428.     return c;
  2429. }
  2430.  
  2431.  
  2432. /*
  2433.  * Get next character from Top Lex File, updating nextchar.
  2434.  */
  2435. short NextC ()
  2436. {
  2437.     short           c;
  2438.  
  2439.     if (!toplf) return EOF;
  2440.     c = toplf->nextchar;
  2441.     toplf->nextchar = NextC1 (toplf->fp);
  2442.     return c;
  2443. }
  2444.  
  2445.  
  2446. /*
  2447.  * Move the top lex file to the next newline.
  2448.  */
  2449. AdvanceEOL ()
  2450. {
  2451.     if (!toplf) return;
  2452.     while (toplf->nextchar != '\n' && !feof (toplf->fp))
  2453.         NextC ();
  2454. }
  2455.  
  2456.  
  2457. /*
  2458.  * Read a raw set of characters updating newline state.  Sets the
  2459.  * fields in rawtok to the type and string pointer, if any. 
  2460.  */
  2461. RawToken ()
  2462. {
  2463.     short           c;
  2464.     short           nochar = 1, comment, nl;
  2465.  
  2466.     bufpos = 0;
  2467.     rawtok.can_be_macro = 1;
  2468.  
  2469.     /*
  2470.      * get a non-blank char 
  2471.      */
  2472.     while (nochar) {
  2473.  
  2474.         /*
  2475.          * get a character testing for end-of-file if EOF, just
  2476.          * return that fact right away 
  2477.          */
  2478.         c = NextC ();
  2479.         if (c == EOF) {
  2480.             rawtok.type = RT_EOF;
  2481.             return;
  2482.         }
  2483.  
  2484.         /*
  2485.          * test next char: if it's white space other than newline,
  2486.          * just clear the newline flag and continue looking. 
  2487.          */
  2488.         if (isspace (c) && c != '\n')
  2489.             toplf->newln = 0;
  2490.         else {
  2491.  
  2492.             /*
  2493.              * otherwise, this is potentially interesting, but it
  2494.              * might be the start of a comment - if so, scan til
  2495.              * the end of the comment (no nested comments ala
  2496.              * "C").  If not, exit the loop. 
  2497.              */
  2498.             if (c == '/' && toplf->nextchar == '*') {
  2499.                 comment = 1;
  2500.                 while (comment) {
  2501.                     c = NextC ();
  2502.                     if (c == '*'
  2503.                       && toplf->nextchar == '/') {
  2504.                         c = NextC ();
  2505.                         comment = 0;
  2506.                     }
  2507.                 }
  2508.  
  2509.             } else nochar = 0;
  2510.         }
  2511.     }
  2512.  
  2513.     nl = toplf->newln;
  2514.     toplf->newln = 0;
  2515.     bufpos = 0;
  2516.  
  2517.     /*
  2518.      * otherwise, read out the cases id symbol starting with char 
  2519.      */
  2520.     if (nl && c == '#')
  2521.         rawtok.type = RT_ESC;
  2522.  
  2523.     else if (c == '\'') {
  2524.         rawtok.chr = NextC ();
  2525.         c = NextC ();
  2526.  
  2527.         /*
  2528.          * Skip past weird character constants, like 'FOOL',
  2529.          * making no attempt to deal with them correctly.
  2530.          */
  2531.         comment = 0;
  2532.         while (c != '\'' && comment < 6) {
  2533.             c = NextC ();
  2534.             comment++;
  2535.         }
  2536.         if (comment >= 6) printf ("error in character constant\n");
  2537.         rawtok.type = RT_CHRC;
  2538.  
  2539.     } else if (isalpha (c) || c == '_') {
  2540.         buf[bufpos++] = c;
  2541.         while (isalnum (toplf->nextchar) || toplf->nextchar == '_')
  2542.             buf[bufpos++] = NextC ();
  2543.         buf[bufpos] = 0;
  2544.         rawtok.type = RT_ID;
  2545.         rawtok.buf = FindString (buf);
  2546.  
  2547.     } else if (c == '\n') {
  2548.         toplf->newln = 1;
  2549.         rawtok.type = RT_NEWLN;
  2550.  
  2551.     } else if (isdigit (c)) {
  2552.         buf[bufpos++] = c;
  2553.         while (isdigit (toplf->nextchar)) buf[bufpos++] = NextC ();
  2554.         buf[bufpos] = 0;
  2555.         rawtok.buf = FindString (buf);
  2556.         rawtok.type = RT_NUM;
  2557.  
  2558.     } else if (c == '"') {
  2559.         while ((c = NextC ()) != '"')
  2560.             if (bufpos < BUFLEN)
  2561.                 buf[bufpos++] = c;
  2562.  
  2563.         if (bufpos + 1 == BUFLEN) printf ("string overflow\n");
  2564.         buf[bufpos] = 0;
  2565.         rawtok.buf = FindString (buf);
  2566.         rawtok.type = RT_STR;
  2567.  
  2568.     } else {
  2569.         rawtok.chr = c;
  2570.         rawtok.type = RT_CHR;
  2571.     }
  2572. }
  2573.  
  2574.  
  2575. /*
  2576.  * Main function -- returns string pointer for next token makes include files
  2577.  * and macros transparent reads from top open file and returns pointer to
  2578.  * string of chars could be a symbol, could be a number, could be a character 
  2579.  */
  2580. short NextToken (tbuf)
  2581.     char          **tbuf;
  2582. {
  2583.     struct Token   *tok;
  2584.     struct Macro   *mac;
  2585.     struct ActiveMacro *amac;
  2586.     short           i;
  2587.     char            c;
  2588.  
  2589.     while () {
  2590.  
  2591.         /*
  2592.          * get raw token from file or active macro 
  2593.          */
  2594.         amac = HEAD (&activemacs);
  2595.         if (retok) {
  2596.             tok = retok;
  2597.             retok = NULL;
  2598.         } else if (TEST (amac)) {
  2599.             tok = amac->curtok;
  2600.             if (!TEST (tok)) {
  2601.                 amac->mac->active = 0;
  2602.                 for (i = 0; i < amac->mac->nargs; i++)
  2603.                     FreeMacro (RemHead (¯os));
  2604.                 Remove (amac);
  2605.                 FREI (amac);
  2606.                 continue;
  2607.             }
  2608.             amac->curtok = NEXT (amac->curtok);
  2609.         } else {
  2610.             RawToken ();
  2611.             tok = &rawtok;
  2612.         }
  2613.         switch (tok->type) {
  2614.             case RT_EOF:
  2615.             if (CloseLexFile ()) {
  2616.                 lasttok = tok;
  2617.                 return RT_EOF;
  2618.             }
  2619.             break;
  2620.             case RT_ESC:
  2621.             RawToken ();
  2622.             if (rawtok.type != RT_ID) {
  2623.                 printf ("real problem with lexer directive\n");
  2624.                 AdvanceEOL ();
  2625.                 break;
  2626.             }
  2627.             if (rawtok.buf == includestr)        Include ();
  2628.             else if (rawtok.buf == definestr)    Define ();
  2629.             else if (rawtok.buf == ifdefstr)    IfDef (1);
  2630.             else if (rawtok.buf == ifndefstr)    IfDef (0);
  2631.             else if (rawtok.buf == elsestr)        Else ();
  2632.             else if (rawtok.buf == endifstr)    EndIf ();
  2633.             else {
  2634.                 printf ("unknown directive \"%s\"\n", rawtok.buf);
  2635.                 AdvanceEOL ();
  2636.             }
  2637.             break;
  2638.  
  2639.             case RT_ID:
  2640.  
  2641.             /*
  2642.              * test for known macros (if this token can be one) 
  2643.              */
  2644.             if (tok->can_be_macro) {
  2645.                 mac = FindMacro (tok->buf);
  2646.                 if (mac) {
  2647.                     InstantiateMacro (mac);
  2648.                     break;
  2649.                 }
  2650.             }
  2651.             case RT_STR:
  2652.             case RT_NUM:
  2653.             *tbuf = tok->buf;
  2654.             lasttok = tok;
  2655.             return tok->type;
  2656.             case RT_CHR:
  2657.             case RT_CHRC:
  2658.             *tbuf = &tok->chr;
  2659.             lasttok = tok;
  2660.             return tok->type;
  2661.         }
  2662.     }
  2663. }
  2664.  
  2665.  
  2666. /*
  2667.  * The given macro has occured in the text, cause it to come into existence
  2668.  * with argument substitution. 
  2669.  */
  2670. InstantiateMacro (mac)
  2671.     struct Macro   *mac;
  2672. {
  2673.     struct ActiveMacro *amac;
  2674.     struct StringElt *arg;
  2675.     struct Macro   *smac;
  2676.     struct Token   *tok;
  2677.     char           *buf;
  2678.     short           vtok, level, endarglist;
  2679.  
  2680.     if (mac->active) {
  2681.         printf ("recursive macro ignored\n");
  2682.         return;
  2683.     }
  2684.  
  2685.     /*
  2686.      * read arguments, if any, and make them macros in their own right
  2687.      * arguments are read recursively using NextToken (right? I'm not
  2688.      * sure...) 
  2689.      */
  2690.     arg = HEAD (&mac->arguments);
  2691.     if (TEST (arg)) {
  2692.  
  2693.         /*
  2694.          * get first open paren 
  2695.          */
  2696.         vtok = NextToken (&buf);
  2697.         if (vtok != RT_CHR || *buf != '(')
  2698.             goto argfail;
  2699.  
  2700.         /*
  2701.          * loop through args separated with commas 
  2702.          */
  2703.         endarglist = 0;
  2704.         while (TEST (arg)) {
  2705.             smac = NEW (struct Macro);
  2706.             if (!smac) goto argfail;
  2707.             smac->name = arg->buf;
  2708.             smac->active = 0;
  2709.             smac->nargs = 0;
  2710.             NewList (&smac->arguments);
  2711.             NewList (&smac->tokens);
  2712.  
  2713.             /*
  2714.              * scan out tokens allowing for nested commas 
  2715.              */
  2716.             level = 0;
  2717.             while () {
  2718.                 vtok = NextToken (&buf);
  2719.                 if (vtok == RT_CHR) {
  2720.                     if (*buf == '(' || *buf == '{')
  2721.                         level++;
  2722.                     if (*buf == '}')
  2723.                         level--;
  2724.                     if (*buf == ')') {
  2725.                         if (level == 0) {
  2726.                             endarglist = 1;
  2727.                             break;
  2728.                         }
  2729.                         level--;
  2730.                     }
  2731.                     if (*buf == ',' && level == 0)
  2732.                         break;
  2733.                 }
  2734.                 tok = CopyToken (lasttok);
  2735.                 if (!tok) goto argfail;
  2736.                 tok->can_be_macro = 0;
  2737.                 AddTail (&smac->tokens, tok);
  2738.             }
  2739.             AddHead (¯os, smac);
  2740.             if (endarglist) break;
  2741.             arg = NEXT (arg);
  2742.         }
  2743.         if (vtok != RT_CHR || *buf != ')') goto argfail;
  2744.     }
  2745.  
  2746.     /*
  2747.      * Macro does not become active in the global list until all
  2748.      * arguments have been parsed and interpreted. 
  2749.      */
  2750.     amac = NEW (struct ActiveMacro);
  2751.     if (!amac) return;        /* what the fuck do I do here? */
  2752.     amac->mac = mac;
  2753.     amac->curtok = HEAD (&mac->tokens);
  2754.     mac->active = 1;
  2755.     AddHead (&activemacs, amac);
  2756.     return;
  2757.  
  2758. argfail:
  2759.     printf ("bad problem with arguments\n");
  2760. }
  2761.  
  2762.  
  2763. /*
  2764.  * locate a specified macro in the available macros list 
  2765.  */
  2766. struct Macro * FindMacro (buf)
  2767.     char           *buf;
  2768. {
  2769.     struct Macro   *mac;
  2770.  
  2771.     for (mac = HEAD (¯os); TEST (mac); mac = NEXT (mac))
  2772.         if (mac->name == buf)
  2773.             return mac;
  2774.  
  2775.     return NULL;
  2776. }
  2777.  
  2778.  
  2779. /*
  2780.  * Do an "include" directive 
  2781.  */
  2782. Include ()
  2783. {
  2784.     short           pos, ok, global;
  2785.     char            c;
  2786.  
  2787.     /*
  2788.      * get filename 
  2789.      */
  2790.     RawToken ();
  2791.     ok = 0;
  2792.     global = 0;
  2793.  
  2794.     if (rawtok.type == RT_STR) {
  2795.         ok = 1;
  2796.         strcpy (basnam, rawtok.buf);
  2797.     } else if (rawtok.type == RT_CHR && rawtok.chr == '<') {
  2798.         ok = 1;
  2799.         pos = 0;
  2800.         while () {
  2801.             c = NextC ();
  2802.             if (c == '>') break;
  2803.             basnam[pos++] = c;
  2804.         }
  2805.         basnam[pos] = 0;
  2806.         global = 1;
  2807.     }
  2808.     AdvanceEOL ();
  2809.     if (!ok) {
  2810.         printf ("no file to include\n");
  2811.         return;
  2812.     }
  2813.     if (!global) if (OpenLexFile (basnam)) return;
  2814.     strcpy (libnam, libbase);
  2815.     strcat (libnam, basnam);
  2816.     if (OpenLexFile (libnam)) return;
  2817.     printf ("error open include file <%s>\n", libnam);
  2818. }
  2819.  
  2820.  
  2821. /*
  2822.  * Do the "define" directive.
  2823.  */
  2824. Define ()
  2825. {
  2826.     struct Macro   *mac = NULL;
  2827.     struct Token   *tok;
  2828.     struct StringElt *se;
  2829.  
  2830.     /*
  2831.      * get identifier to define 
  2832.      */
  2833.     RawToken ();
  2834.     if (rawtok.type != RT_ID) {
  2835.         printf ("error in define - no identifier\n");
  2836.         goto escape;
  2837.     }
  2838.     mac = NEW (struct Macro);
  2839.     if (!mac) goto escape;
  2840.     mac->name = rawtok.buf;
  2841.     mac->active = 0;
  2842.     mac->nargs = 0;
  2843.     NewList (&mac->arguments);
  2844.     NewList (&mac->tokens);
  2845.  
  2846.     /*
  2847.      * Look for parenthized argument list.
  2848.      */
  2849.     if (toplf->nextchar == '(') {
  2850.         RawToken ();
  2851.         while () {
  2852.             RawToken ();
  2853.  
  2854.             /*
  2855.              * deal with special case of "#define MACRO()" 
  2856.              */
  2857.             if (!mac->nargs && rawtok.type == RT_CHR
  2858.               && rawtok.chr == ')')
  2859.                 break;
  2860.             if (rawtok.type != RT_ID) {
  2861.                 printf ("macro argument not an identifier\n");
  2862.                 goto escape;
  2863.             }
  2864.             se = NEW (struct StringElt);
  2865.             if (!se) goto escape;
  2866.             se->buf = rawtok.buf;
  2867.             AddTail (&mac->arguments, se);
  2868.             mac->nargs++;
  2869.             RawToken ();
  2870.             if (rawtok.type != RT_CHR) {
  2871.                 printf ("macro arg list delimiter not a character\n");
  2872.                 goto escape;
  2873.             }
  2874.             if (rawtok.chr == ')') break;
  2875.             if (rawtok.chr != ',') {
  2876.                 printf ("macro arg list delimiter not ',' or ')'\n");
  2877.                 goto escape;
  2878.             }
  2879.         }
  2880.     }
  2881.  
  2882.     /*
  2883.      * get the sequence of tokens which make up this definition 
  2884.      */
  2885.     while () {
  2886.         RawToken ();
  2887.         if (rawtok.type == RT_NEWLN || rawtok.type == RT_EOF) break;
  2888.         tok = CopyToken (&rawtok);
  2889.         if (!tok) goto escape;
  2890.         tok->can_be_macro = 1;
  2891.         AddTail (&mac->tokens, tok);
  2892.     }
  2893.  
  2894.     AddHead (¯os, mac);
  2895.     return;
  2896.  
  2897. escape:
  2898.     if (mac) FreeMacro (mac);
  2899.     AdvanceEOL ();
  2900. }
  2901.  
  2902.  
  2903. /*
  2904.  * Skip over a defined-out section.  Returns 1 if it ends with #else, 0
  2905.  * if it ends with #endif, -1 for EOF.  Keeps track of nested if's
  2906.  * being skipped.
  2907.  */
  2908. short SkipRemovedSec ()
  2909. {
  2910.     short           level = 0;
  2911.  
  2912.     while () {
  2913.         RawToken ();
  2914.         if (rawtok.type == RT_EOF) return -1;
  2915.         if (rawtok.type == RT_ESC) {
  2916.             RawToken ();
  2917.             if (rawtok.type == RT_ID) {
  2918.                 if (rawtok.buf == ifdefstr
  2919.                   || rawtok.buf == ifndefstr) level++;
  2920.                 if (rawtok.buf == elsestr)
  2921.                     if (!level) return 1;
  2922.                 if (rawtok.buf == endifstr)
  2923.                     if (!level--) return 0;
  2924.             }
  2925.         }
  2926.     }
  2927. }
  2928.  
  2929.  
  2930. /*
  2931.  * Does the "ifdef"/"ifndef" - "else" - "endif" directive.  Type is 1
  2932.  * for ifdef and 0 for ifndef.
  2933.  */
  2934. IfDef (type)
  2935.     short           type;
  2936. {
  2937.     struct Macro   *mac;
  2938.     struct IfBlock *ib;
  2939.     short           els;
  2940.  
  2941.     RawToken ();
  2942.     mac = FindMacro (rawtok.buf);
  2943.  
  2944.     /*
  2945.      * If condition is true, add a node to say "skip the next #else
  2946.      * section" if false, skip this section.  If section ends with an
  2947.      * else, add a junk node to pop off for consistency at next #endif.
  2948.      */
  2949.     if ((mac && type) || (!mac && !type)) {
  2950.         ib = NEW (struct IfBlock);
  2951.         if (!ib) return;
  2952.         ib->skip_else = 1;
  2953.         AddHead (&ifblocks, ib);
  2954.     } else {
  2955.         els = SkipRemovedSec ();
  2956.         if (els == 1) {
  2957.             ib = NEW (struct IfBlock);
  2958.             if (!ib) return;
  2959.             ib->skip_else = 0;
  2960.             AddHead (&ifblocks, ib);
  2961.         }
  2962.     }
  2963. }
  2964.  
  2965.  
  2966. Else ()
  2967. {
  2968.     struct IfBlock *ib;
  2969.     short           els;
  2970.  
  2971.     ib = HEAD (&ifblocks);
  2972.     ASSERT (TEST (ib));
  2973.     Remove (ib);
  2974.     if (!ib->skip_else) {
  2975.         printf ("strange \"else\" block found\n");
  2976.     } else {
  2977.         els = SkipRemovedSec ();
  2978.         if (els == 1 && ib->skip_else) {
  2979.             printf ("strange \"else\" block found\n");
  2980.         }
  2981.     }
  2982.     FREI (ib);
  2983. }
  2984.  
  2985.  
  2986. EndIf ()
  2987. {
  2988.     struct IfBlock *ib;
  2989.  
  2990.     ib = HEAD (&ifblocks);
  2991.     ASSERT (TEST (ib));
  2992.     Remove (ib);
  2993.     FREI (ib);
  2994. }
  2995.  
  2996.  
  2997. /*
  2998.  * Backs up one token 
  2999.  */
  3000. Backspace ()
  3001. {
  3002.     retok = lasttok;
  3003. }
  3004.  
  3005.  
  3006. struct Token * CopyToken (tok)
  3007.     struct Token   *tok;
  3008. {
  3009.     struct Token   *ntok;
  3010.  
  3011.     ntok = NEW (struct Token);
  3012.     if (ntok) *ntok = *tok;
  3013.     return ntok;
  3014. }
  3015.  
  3016.  
  3017.  
  3018. /* HASH TABLE STUFF */
  3019.  
  3020.  
  3021. InitHash ()
  3022. {
  3023.     short           i;
  3024.  
  3025.     for (i = 0; i < NHASH; i++)
  3026.         NewList (&hashTab[i]);
  3027. }
  3028.  
  3029.  
  3030. /*
  3031.  * Simple but (I hope) effective hash function.
  3032.  */
  3033. short HashVal (buf)
  3034.     char           *buf;
  3035. {
  3036.     unsigned long   hash;
  3037.  
  3038.     hash = 0;
  3039.     while (*buf) hash = hash * 3 + *buf++;
  3040.     return hash % NHASH;
  3041. }
  3042.  
  3043.  
  3044. /*
  3045.  * Finds (or creates) a stored string matching the given one return pointer
  3046.  * which acts as unique ident for this string.
  3047.  */
  3048. char * FindString (buf)
  3049.     char           *buf;
  3050. {
  3051.     short           hash;
  3052.     struct Lst     *hlist;
  3053.     struct StringElt *se;
  3054.     char           *sto;
  3055.  
  3056.     hash = HashVal (buf);
  3057.     hlist = &hashTab[hash];
  3058.  
  3059.     for (se = HEAD (hlist); TEST (se); se = NEXT (se)) {
  3060.         if (!strcmp (se->buf, buf))
  3061.             return se->buf;
  3062.     }
  3063.     se = NEW (struct StringElt);
  3064.     if (!se) return NULL;
  3065.     sto = NEW_N (char, strlen (buf) + 1);
  3066.     if (!sto) {
  3067.         FREI (se);
  3068.         return NULL;
  3069.     }
  3070.     AddTail (hlist, se);
  3071.     strcpy (sto, buf);
  3072.     se->buf = sto;
  3073.     return sto;
  3074. }
  3075.  
  3076.  
  3077. FreeHash ()
  3078. {
  3079.     short           i;
  3080.     struct StringElt *se, *nse;
  3081.  
  3082.     for (i = 0; i < NHASH; i++) {
  3083.         for (se = HEAD (&hashTab[i]); nse = NEXT (se); se = nse) {
  3084.             FREE_N (se->buf, char, strlen (se->buf) + 1);
  3085.             FREI (se);
  3086.         }
  3087.     }
  3088. }
  3089. SHAR_EOF
  3090. cat << \SHAR_EOF > lex.h
  3091. #define RT_EOF 0
  3092. #define RT_ESC 1
  3093. #define RT_STR 2
  3094. #define RT_NUM 3
  3095. #define RT_ID  4
  3096. #define RT_CHR   5
  3097. #define RT_CHRC  6
  3098. #define RT_NEWLN 7
  3099. SHAR_EOF
  3100. cat << \SHAR_EOF > listmac.h
  3101. /* My MinNode definitions, basically, but with names
  3102.  * that I like better for C applications
  3103.  */
  3104. struct Nod {
  3105.    struct Nod *next,*prev;
  3106. };
  3107.  
  3108. struct Lst {
  3109.    struct Nod *head,*tail,*tailprev;
  3110. };
  3111.  
  3112. /* some macros for dealing with Exec-like lists
  3113.  *    HEAD(lst)   struct Lst *lst:  gives first node
  3114.  *    TAIL(lst)   struct Lst *lst:  gives last node
  3115.  *    NEXT(nod)   struct Nod *nod:  gives next element
  3116.  *    PREV(nod)   struct Nod *nod:  gives previous element
  3117.  *    TEST(nod)   struct Nod *nod:  is non-zero for valid nodes
  3118.  *    OFF1(nod)   struct Nod *nod:  offsets backward one node
  3119.  */
  3120. #define HEAD(lst)    (void *)(((struct Lst*)(lst))->head)
  3121. #define TAIL(lst)    (void *)(((struct Lst*)(lst))->tailprev)
  3122. #define NEXT(nod)    (void *)(((struct Nod *)(nod))->next)
  3123. #define PREV(nod)    (void *)(((struct Nod *)(nod))->prev)
  3124. #define TEST(nod)    NEXT(nod)
  3125. #define OFF1(nod)    (void *)((char *)(nod)-sizeof(struct Nod))
  3126. SHAR_EOF
  3127. cat << \SHAR_EOF > macros.r
  3128. /*
  3129.  * Standard set of macros for Modeler requesters.  Gives them a 
  3130.  * consistent look (if nothing else).
  3131.  */
  3132.  
  3133. /* Ok/Cancel gadgets, numbers "ok" and "can"
  3134.  * It's an hbox with the gadgets pressed apart with a filler.
  3135.  * Also has the extended gadget data for these gadgets.
  3136.  */
  3137. #define OKCAN(ok,can) \
  3138.     (h|(v-(v(b 0 3)(h(b 5 0)(t 2"Ok")(b 5 0))(b 0 2)):ok-)| \
  3139.      (b 5 0) f (b 5 0) \
  3140.       |(v-(v(b 0 3)(h (b 5 0)(t 3"Cancel")(b 5 0))(b 0 2)):can-)|)
  3141. #define OKCAN_EXT(ok,can) \
  3142.     ok:"OK_ID""ev" can:"CAN_ID""ev"
  3143.  
  3144. /* An underlined title string centered in an hbox.
  3145.  */
  3146. #define TITLE(str)    (h f(v(t 3 str)(-1 3))f)
  3147.  
  3148. /* Text centered in a vbox -- for labeling larger boxes.
  3149.  */
  3150. #define LABEL(txt)    (v f(t txt)f)
  3151.  
  3152. /* String gadget with a more complex box around it.  Starting text is
  3153.  * "txt", gadget number is "num."
  3154.  */
  3155. #define STRGAD(txt,num) \
  3156.     (h|(v-(-1 2)(h(|1 2)(|1 2)(t txt):num(|1 2)(|1 2))(-1 2)-)|)
  3157.  
  3158. /* Scale gadgets.  The +/- buttons and scale indicator.
  3159.  */
  3160. #define SCALE(up,down) \
  3161.     (v f \
  3162.       (h f (t 3"Scale") f) \
  3163.       (h (t 1"+"):up f (t 1"mm") f (t 1"-"):down) \
  3164.      f)
  3165. #define SCALE_EXT(up,down)    up:"SCLU_ID""v" down:"SCLD_ID""v"
  3166.  
  3167. /* Does the boilerplate to start and end a requester.  The boxes between
  3168.  * start and end will lie within a vbox with a nice border around it.
  3169.  */
  3170. #define START_REQ    (h | (v - (h (b 8 0) (v (b 0 8)
  3171. #define END_REQ        (b 0 8))(b 8 0)) - ) | )
  3172.  
  3173. /* Layer gadgets centered in an hbox.
  3174.  * Always numbered 10-17.
  3175.  */
  3176. #define LAYGAD \
  3177.     (h f (v f(t 1"Layer")f) (b 5 0) | \
  3178.      (v - (b 0 3) (h (b 2 0) \
  3179.       (t"1"):10(b 2 0)(t"2"):11(b 2 0)(t"3"):12(b 2 0)(t"4"):13(b 2 0) \
  3180.       (t"5"):14(b 2 0)(t"6"):15(b 2 0)(t"7"):16(b 2 0)(t"8"):17(b 2 0) \
  3181.      ) (b 0 2) -) \
  3182.     | f)
  3183. #define LAYGAD_EXT \
  3184.     10:"BUF_ID""ti"   11:"BUF_ID+1""ti"\
  3185.     12:"BUF_ID+2""ti" 13:"BUF_ID+3""ti"\
  3186.     14:"BUF_ID+4""ti" 15:"BUF_ID+5""ti"\
  3187.     16:"BUF_ID+6""ti" 17:"BUF_ID+7""ti"
  3188. SHAR_EOF
  3189. cat << \SHAR_EOF > pro.c
  3190. #include <stdio.h>
  3191. #include <functions.h>
  3192. #include <exec/types.h>
  3193. #include <intuition/intuition.h>
  3194.  
  3195. #define OK_ID  0x80
  3196. #define CAN_ID 0x81
  3197. #define STR_ID 0x82
  3198. #define NUMCHR 20
  3199. struct TextAttr ta = { (UBYTE*)"topaz.font",8,0,0 };
  3200. UBYTE undo[NUMCHR];
  3201.  
  3202. #define REQ col_req
  3203. #include "prototype.h"
  3204.  
  3205.  
  3206. struct NewWindow nwin = {
  3207.    0,0,300,150,
  3208.    -1,-1,GADGETUP|GADGETDOWN|REQCLEAR|MOUSEMOVE,
  3209.    WINDOWDEPTH|WINDOWDRAG|SMART_REFRESH,
  3210.    NULL,NULL,(UBYTE*)"Blocks",NULL,
  3211.    NULL,0,0,0,0,WBENCHSCREEN
  3212. };
  3213.  
  3214. struct IntuitionBase *IntuitionBase;
  3215.  
  3216.  
  3217. main(argc,argv)
  3218.    int argc;
  3219.    char *argv[];
  3220. {
  3221.    struct Window *win;
  3222.  
  3223.    IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L);
  3224.    if (IntuitionBase) {
  3225.       nwin.Width = REQ.Width + 10;
  3226.       nwin.Height = REQ.Height + 14;
  3227.       win = OpenWindow (&nwin);
  3228.       if (win) {
  3229.          Body (win);
  3230.          CloseWindow (win);
  3231.       }
  3232.       CloseLibrary (IntuitionBase);
  3233.    }
  3234. }
  3235.  
  3236.  
  3237.  
  3238. Body(win)
  3239.    struct Window *win;
  3240. {
  3241.    struct IntuiMessage *im;
  3242.    BOOL looping=TRUE;
  3243.    ULONG class;
  3244.  
  3245. /* set up proportional gadgets
  3246.    scol_pinfo[0].HorizBody = ((long)0xffff)/16;
  3247.    scol_pinfo[1].HorizBody = ((long)0xffff)/16;
  3248.    scol_pinfo[2].HorizBody = ((long)0xffff)/16;
  3249.  */
  3250.    REQ.LeftEdge = 5;
  3251.    REQ.TopEdge = 12;
  3252.    Request (&REQ,win);
  3253.  
  3254.    while (looping) {
  3255.       im = (struct IntuiMessage *) GetMsg(win->UserPort);
  3256.       if (!im) WaitPort (win->UserPort);
  3257.        else {
  3258.          printf ("Message: %lx\n", im);
  3259.          class = im->Class;
  3260.          ReplyMsg (im);
  3261.          if (class == REQCLEAR) looping = FALSE;
  3262.        }
  3263.    }
  3264. }
  3265. SHAR_EOF
  3266. cat << \SHAR_EOF > std.h
  3267. #define NEW(typ) (typ*)AllocMem((long)sizeof(typ),0L)
  3268. #define FREE(p,typ) FreeMem(p,(long)sizeof(typ))
  3269.  
  3270. #define NEW_N(typ,n) (typ*)AllocMem((long)((n)*sizeof(typ)),0L)
  3271. #define FREE_N(p,typ,n) FreeMem(p,(long)((n)*sizeof(typ)))
  3272.  
  3273. #define NEW_X(typ,x) (typ*)AllocMem((long)(sizeof(typ)+(x)),0L)
  3274. #define FREE_X(p,typ,x) FreeMem(p,(long)(sizeof(typ)+(x)))
  3275.  
  3276. #define FREI(p) FreeMem(p,(long)sizeof(*p))
  3277.  
  3278. #ifndef FINAL_VERSION
  3279. #define ASSERT(c) if (!(c)) printf ("assert failure in %s (%s : %d)\n", \
  3280.    __FUNC__,__FILE__,__LINE__)
  3281. #else
  3282. #define ASSERT(c)
  3283. #endif
  3284.  
  3285. #define MAKE_ID(a,b,c,d) (((long)a<<24)|((long)b<<16)|((long)c<<8)|d)
  3286. SHAR_EOF
  3287. #    End of shell archive
  3288. exit 0
  3289. -- 
  3290. Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
  3291. Have five nice days.
  3292.